diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 7daf311..7580167 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -3541,3 +3541,80 @@ function drupal_check_memory_limit($required, $memory_limit = NULL) {
   //   the operation.
   return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required)));
 }
+
+/**
+ * Instantiates and statically caches the correct class for a PHP loader.
+ *
+ * By default, this returns an instance of the Drupal\Component\PhpLoader\MTimeProtectedLoader
+ * class.
+ *
+ * Classes implementing Drupal\Component\PhpLoader\PhpLoaderInterface can
+ * register themselves both as a default implementation and for specific bins.
+ *
+ * @param $bin
+ *   The bin for which the loader object should be returned. Defaults to
+ *   'default'.
+ *
+ * @return Drupal\Component\PhpLoader\PhpLoaderInterface
+ *   The loader object associated with the specified bin.
+ *
+ * @see Drupal\Component\PhpLoader\PhpLoaderInterface
+ */
+function _drupal_php_get_loader($bin) {
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['loader_objects'] = &drupal_static(__FUNCTION__);
+  }
+  $loader_objects = &$drupal_static_fast['loader_objects'];
+  if (!isset($loader_objects[$bin])) {
+    $loader_backends = variable_get('loader_classes', array('default' =>
+      array(
+        'class' => 'Drupal\Component\PhpLoader\MTimeProtectedLoader',
+        'prefix' => variable_get('file_public_path', conf_path() . '/files') . '/codegen',
+        'secret' => $GLOBALS['drupal_hash_salt'],
+      ),
+    ));
+    $loader = isset($loader_backends[$bin]) ? $loader_backends[$bin] : $loader_backends['default'];
+    $class = $loader['class'];
+    $loader_objects[$bin] = new $class($loader);
+  }
+  return $loader_objects[$bin];
+}
+
+/**
+ * include a PHP file.
+ *
+ * @param string $filename
+ *   The filename. Can be a relative path.
+ * @param string $bin
+ *   An optional bin. Separate bins can use a different loader class.
+ */
+function drupal_php_include($filename, $bin = 'default') {
+  return _drupal_php_get_loader($bin)->phpInclude($filename);
+}
+
+/**
+ * Write a PHP file.
+ *
+ * @param string $filename
+ *   The filename. Can be a relative path.
+ * @param string $data
+ *   The PHP code to be written.
+ * @param string $bin
+ *   An optional bin. Separate bins can use a different loader class.
+ */
+function drupal_php_write($filename, $data, $bin = 'default') {
+  return _drupal_php_get_loader($bin)->write($filename, $data);
+}
+
+/**
+ * Delete a PHP file.
+ *
+ * @param string $filename
+ *   The filename to be deleted. Can be a relative path.
+ * @param string $bin
+ *   An optional bin. Separate bins can use a different loader class.
+ */
+function drupal_php_delete($filename, $bin = 'default') {
+  return _drupal_php_get_loader($bin)->delete($filename);
+}
diff --git a/core/lib/Drupal/Component/PhpLoader/MTimeProtectedLoader.php b/core/lib/Drupal/Component/PhpLoader/MTimeProtectedLoader.php
new file mode 100644
index 0000000..1bce4d6
--- /dev/null
+++ b/core/lib/Drupal/Component/PhpLoader/MTimeProtectedLoader.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace Drupal\Component\PhpLoader;
+
+class MTimeProtectedLoader implements PhpLoaderInterface {
+  /**
+   * The path to the directory containing the files.
+   *
+   * @var string
+   */
+  protected $prefix;
+
+  /**
+   * The secret used in the HMAC.
+   *
+   * @var string
+   */
+  protected $secret;
+
+  /**
+   * Constructs this MTimeProtectedLoader object.
+   *
+   * @param $config
+   *   An associated array, containing at least three keys (the rest are
+   *   ignored):
+   *   - prefix: The path to the directory containing the files.
+   *   - secret: A cryptographically hard to guess secret string.
+   */
+  public function __construct(array $config) {
+    $this->prefix = $config['prefix'];
+    $this->secret = $config['secret'];
+  }
+
+  /**
+   * Implements Drupal\Component\PhpLoader\PhpLoaderInterface::phpInclude()
+   */
+  public function phpInclude($filename) {
+    $filename = str_replace('/', '#', $filename);
+    $dir = $this->prefix . '/' . $filename;
+    $filename = $this->getPath($dir, $filename, filemtime($dir));
+    if (file_exists($filename)) {
+      include_once $filename;
+      return TRUE;
+    }
+  }
+
+  /**
+   * Implements Drupal\Component\PhpLoader\PhpLoaderInterface::write()
+   */
+  public function write($filename, $data) {
+    $filename = str_replace('/', '#', $filename);
+    $original_path = "$this->prefix/.$filename";
+    if (!file_exists($this->prefix)) {
+      mkdir($this->prefix, 0700, TRUE);
+      chmod($this->prefix, 0700);
+    }
+    if (!@file_put_contents($original_path, $data)) {
+      return FALSE;
+    }
+    chmod($original_path, 0400);
+    $dir = $this->prefix . '/' . $filename;
+    if (file_exists($dir)) {
+      $this->cleanDir($dir);
+      touch($dir);
+    }
+    else {
+      mkdir($dir);
+    }
+    $previous_mtime = 0;
+    $loop = 0;
+    // Now move the file to its final place. The mtime of a directory is the
+    // time of the last file create or delete in the directory. So the moving
+    // will update the directory mtime. This update will very likely not show
+    // up in filemtime, however, because it has a coarse, one second
+    //  granularity and typical moves takes significantly less than that. In
+    // the unlucky case it does, we need to redo the rename to a new filename
+    // because read() expects the filemtime to be less or equal to the
+    // directory mtime. So renaming needs to happen in a loop. Also note that
+    // clearstatcache() returns NULL so it does not affect the loop condition.
+    while (clearstatcache() || (($mtime = filemtime($dir)) && $previous_mtime != $mtime)) {
+      $previous_mtime = $mtime;
+      chmod($dir, 0300);
+      // Reset the file back in the original place if this is not the first
+      // iteration.
+      if ($loop) {
+        rename($full_path, $original_path);
+        // Make sure to not to have an infinite loop on a hopelessly slow
+        // filesystem.
+        if ($loop > 10) {
+          unlink($original_path);
+          return FALSE;
+        }
+      }
+      $full_path = $this->getPath($dir, $filename, $mtime);
+      rename($original_path, $full_path);
+      chmod($dir, 0100);
+      $loop++;
+    }
+  }
+
+  /**
+   * Implements Drupal\Component\PhpLoader\PhpLoaderInterface::delete()
+   */
+  public function delete($filename) {
+    $dir = $this->prefix . '/' . str_replace('/', '#', $filename);
+    $this->cleanDir($dir);
+    rmdir($dir);
+  }
+
+  /**
+   * Removes everything in a directory, leaving it empty.
+   */
+  protected function cleanDir($dir) {
+    chmod($dir, 0700);
+    foreach (new DirectoryIterator($dir, FilesystemIterator::SKIP_DOTS) as $file) {
+      unlink($file->getPathName());
+    }
+  }
+
+  /**
+   * Constructs the secret path based on the filename and the mtime.
+   *
+   * @param string $dir
+   * @param string $filename
+   * @param int $dir_mtime
+   */
+  protected function getPath($dir, $filename, $dir_mtime) {
+    return $dir . '/' . hash_hmac('sha256', $filename, $this->secret . $dir_mtime) . '.php';
+  }
+}
diff --git a/core/lib/Drupal/Component/PhpLoader/NativeLoader.php b/core/lib/Drupal/Component/PhpLoader/NativeLoader.php
new file mode 100644
index 0000000..2a2227c
--- /dev/null
+++ b/core/lib/Drupal/Component/PhpLoader/NativeLoader.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\Component\PhpLoader\NativeLoader
+ */
+
+namespace Drupal\Component\PhpLoader;
+
+/**
+ * The simplest PHP loader: just uses the native methods.
+ */
+class NativeLoader implements PhpLoaderInterface {
+  protected $prefix;
+
+
+  /**
+   * Constructs this NativeLoader object.
+   *
+   * @param $config
+   *   An associated array, containing at least one key (the rest are ignored):
+   *   - prefix: The path to the directory containing the files.
+   */
+  public function __construct(array $config) {
+    $this->prefix = $config['prefix'];
+  }
+
+  /**
+   * Implements Drupal\Component\PhpLoader\PhpLoaderInterface::include()
+   */
+  public function phpInclude($filename) {
+    include_once $this->prefix . '/' . $filename;
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Component\PhpLoader\PhpLoaderInterface::write()
+   */
+  public function write($filename, $data) {
+    return file_put_contents($this->prefix . '/' . $filename, $data);
+  }
+
+  /**
+   * Implements Drupal\Component\PhpLoader\PhpLoaderInterface::delete()
+   */
+  public function delete($filename) {
+    unlink($filename);
+  }
+}
diff --git a/core/lib/Drupal/Component/PhpLoader/PhpLoaderInterface.php b/core/lib/Drupal/Component/PhpLoader/PhpLoaderInterface.php
new file mode 100644
index 0000000..52bbb08
--- /dev/null
+++ b/core/lib/Drupal/Component/PhpLoader/PhpLoaderInterface.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\Component\PhpLoader;
+
+interface PhpLoaderInterface {
+  /**
+   * Include a PHP file.
+   *
+   * @param string $filename
+   *   The filename. Can be a relative path.
+   *
+   * @return
+   *   TRUE if the include was successful.
+   */
+  public function phpInclude($filename);
+
+  /**
+   * Write a PHP file.
+   *
+   * @param string $filename
+   *   The filename. Can be a relative path.
+   * @param string $data
+   *    The PHP code to be written.
+   *
+   * @return
+   *   FALSE if the write failed.
+   */
+  public function write($filename, $data);
+
+  /**
+   * Delete a PHP file.
+   *
+   * @param string $filename
+   *   The filename to be deleted. Can be a relative path.
+   * @param string $bin
+   *   An optional bin. Separate bins can use a different loader class.
+   */
+  public function delete($filename);
+}
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index cccd011..5595a60 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -761,7 +761,7 @@ abstract class TestBase {
     }
 
     // Delete temporary files directory.
-    file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10));
+    _simpletest_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10));
 
     // Restore original database connection.
     Database::removeConnection('default');
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 4138916..3518977 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -507,7 +507,7 @@ function simpletest_clean_temporary_directories() {
     foreach ($files as $file) {
       $path = 'public://simpletest/' . $file;
       if (is_dir($path) && (is_numeric($file) || strpos($file, 'config_simpletest') !== FALSE)) {
-        file_unmanaged_delete_recursive($path);
+        _simpletest_delete_recursive($path);
         $count++;
       }
     }
@@ -522,6 +522,35 @@ function simpletest_clean_temporary_directories() {
 }
 
 /**
+ * Recursively delete a directory regardless of permissions (if possible).
+ *
+ * @param $path
+ *   The path that will be obliterated.
+ */
+function _simpletest_delete_recursive($path) {
+  // We can not use file_unmanaged_delete_recursive because it
+  // deliberately only removes visible files with write permission.
+  chmod($path, 0700);
+  // We can't use a RecursiveDirectoryIterator either because the directories
+  // might need chmod first.
+  if (is_dir($path)) {
+    $dir = dir($path);
+    while (($entry = $dir->read()) !== FALSE) {
+      if ($entry == '.' || $entry == '..') {
+        continue;
+      }
+      $entry_path = $path . '/' . $entry;
+      _simpletest_delete_recursive($entry_path);
+    }
+    $dir->close();
+    rmdir($path);
+  }
+  else {
+    unlink($path);
+  }
+}
+
+/**
  * Clear the test result tables.
  *
  * @param $test_id
diff --git a/core/modules/system/lib/Drupal/system/Tests/PhpLoader/MTimeProtectedLoaderTest.php b/core/modules/system/lib/Drupal/system/Tests/PhpLoader/MTimeProtectedLoaderTest.php
new file mode 100644
index 0000000..436d59f
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/PhpLoader/MTimeProtectedLoaderTest.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\PhpLoader\MTimeProtectedLoaderTest
+ */
+
+namespace Drupal\system\Tests\PhpLoader;
+
+use Drupal\simpletest\WebTestBase;
+
+class MTimeProtectedLoaderTest extends WebTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Loader test',
+      'description' => 'Tests the protected loader.',
+      'group' => 'System',
+    );
+  }
+
+  public function testMTimeProtectedLoader() {
+    variable_set('loader_classes', array('default' =>
+      array(
+        'class' => 'Drupal\Component\PhpLoader\MTimeProtectedLoader',
+        'prefix' => variable_get('file_public_path', conf_path() . '/files') . '/codegen',
+        'secret' => $GLOBALS['drupal_hash_salt'],
+      ),
+    ));
+    drupal_static_reset('_drupal_php_get_loader');
+    $filename = $this->randomName() . '/' . $this->randomName() . '.php';
+    do {
+      $random = mt_rand(10000, 100000);
+      $function = 'test' . $random;
+    } while (function_exists($function));
+    $contents = "<?php\nfunction $function() { return $random;}";
+    drupal_php_write($filename, $contents);
+    drupal_php_include($filename);
+    $this->assertIdentical($function(), $random);
+  }
+}
