public function testGetItemPath() { $loader = new ClassLoader(); $loader->pushManifest($this->testManifest1); $this->assertEquals(realpath($this->baseManifest1 . '/module/classes/ClassA.php'), realpath($loader->getItemPath('ClassA'))); $this->assertEquals(false, $loader->getItemPath('UnknownClass')); $this->assertEquals(false, $loader->getItemPath('OtherClassA')); $loader->pushManifest($this->testManifest2); $this->assertEquals(false, $loader->getItemPath('ClassA')); $this->assertEquals(false, $loader->getItemPath('UnknownClass')); $this->assertEquals(realpath($this->baseManifest2 . '/module/classes/OtherClassA.php'), realpath($loader->getItemPath('OtherClassA'))); }
function executeInSubprocess($includeStderr = false) { // Get the path to the ErrorControlChain class $erroControlClass = 'SilverStripe\\Core\\Startup\\ErrorControlChain'; $classpath = ClassLoader::instance()->getItemPath($erroControlClass); $suppression = $this->suppression ? 'true' : 'false'; // Start building a PHP file that will execute the chain $src = '<' . "?php\nrequire_once '{$classpath}';\n\n\$chain = new {$erroControlClass}();\n\n\$chain->setSuppression({$suppression});\n\n\$chain\n"; // For each step, use reflection to pull out the call, stick in the the PHP source we're building foreach ($this->steps as $step) { $func = new ReflectionFunction($step['callback']); $source = file($func->getFileName()); $start_line = $func->getStartLine() - 1; $end_line = $func->getEndLine(); $length = $end_line - $start_line; $src .= implode("", array_slice($source, $start_line, $length)) . "\n"; } // Finally add a line to execute the chain $src .= "->execute();"; // Now stick it in a temporary file & run it $codepath = TEMP_FOLDER . '/ErrorControlChainTest_' . sha1($src) . '.php'; if ($includeStderr) { $null = '&1'; } else { $null = is_writeable('/dev/null') ? '/dev/null' : 'NUL'; } file_put_contents($codepath, $src); exec("php {$codepath} 2>{$null}", $stdout, $errcode); unlink($codepath); return array(implode("\n", $stdout), $errcode); }
public function tearDown() { parent::tearDown(); ClassLoader::instance()->popManifest(); }
/** * Updates the database schema, creating tables & fields as necessary. */ public function build() { // The default time limit of 30 seconds is normally not enough increase_time_limit_to(600); // Get all our classes ClassLoader::instance()->getManifest()->regenerate(); $url = $this->getReturnURL(); if ($url) { echo "<p>Setting up the database; you will be returned to your site shortly....</p>"; $this->doBuild(true); echo "<p>Done!</p>"; $this->redirect($url); } else { $quiet = $this->request->requestVar('quiet') !== null; $fromInstaller = $this->request->requestVar('from_installer') !== null; $populate = $this->request->requestVar('dont_populate') === null; $this->doBuild($quiet || $fromInstaller, $populate); } }
/** * Pushes a class and template manifest instance that include tests onto the * top of the loader stacks. */ public static function use_test_manifest() { $flush = true; if (isset($_GET['flush']) && $_GET['flush'] === '0') { $flush = false; } $classManifest = new ClassManifest(BASE_PATH, true, $flush); ClassLoader::instance()->pushManifest($classManifest, false); SapphireTest::set_test_class_manifest($classManifest); ThemeResourceLoader::instance()->addSet('$default', new ThemeManifest(BASE_PATH, project(), true, $flush)); Config::inst()->pushConfigStaticManifest(new ConfigStaticManifest(BASE_PATH, true, $flush)); // Invalidate classname spec since the test manifest will now pull out new subclasses for each internal class // (e.g. Member will now have various subclasses of DataObjects that implement TestOnly) DataObject::reset(); }
gc_enable(); // Initialise the dependency injector as soon as possible, as it is // subsequently used by some of the following code $injector = new Injector(array('locator' => 'SilverStripe\\Core\\Injector\\SilverStripeServiceConfigurationLocator')); Injector::set_inst($injector); /////////////////////////////////////////////////////////////////////////////// // MANIFEST // Regenerate the manifest if ?flush is set, or if the database is being built. // The coupling is a hack, but it removes an annoying bug where new classes // referenced in _config.php files can be referenced during the build process. $requestURL = isset($_REQUEST['url']) ? trim($_REQUEST['url'], '/') : false; $flush = isset($_GET['flush']) || $requestURL === trim(BASE_URL . '/dev/build', '/'); global $manifest; $manifest = new ClassManifest(BASE_PATH, false, $flush); // Register SilverStripe's class map autoload $loader = ClassLoader::instance(); $loader->registerAutoloader(); $loader->pushManifest($manifest); // Now that the class manifest is up, load the static configuration $configManifest = new ConfigStaticManifest(); Config::inst()->pushConfigStaticManifest($configManifest); // And then the yaml configuration $configManifest = new ConfigManifest(BASE_PATH, false, $flush); Config::inst()->pushConfigYamlManifest($configManifest); // Load template manifest SilverStripe\View\ThemeResourceLoader::instance()->addSet('$default', new SilverStripe\View\ThemeManifest(BASE_PATH, project(), false, $flush)); // If in live mode, ensure deprecation, strict and notices are not reported if (Director::isLive()) { error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE)); } ///////////////////////////////////////////////////////////////////////////////
/** * Returns all classes contained in a certain folder. * * @todo Doesn't return additional classes that only begin * with the filename, and have additional naming separated through underscores. * * @param string $folderPath Relative or absolute folder path * @return array Array of class names */ public static function classes_for_folder($folderPath) { $absFolderPath = Director::getAbsFile($folderPath); $matchedClasses = array(); $manifest = ClassLoader::instance()->getManifest()->getClasses(); foreach ($manifest as $class => $compareFilePath) { if (stripos($compareFilePath, $absFolderPath) === 0) { $matchedClasses[] = $class; } } return $matchedClasses; }
/** * Test ability for textcollector to detect modules */ public function testModuleDetection() { ClassLoader::instance()->pushManifest($this->manifest); $collector = new i18nTextCollectorTest_Collector(); $modules = $collector->getModules_Test($this->alternateBasePath); $this->assertEquals(array('i18nnonstandardmodule', 'i18nothermodule', 'i18ntestmodule', 'themes/testtheme1', 'themes/testtheme2'), $modules); $this->assertEquals('i18ntestmodule', $collector->findModuleForClass_Test('i18nTestNamespacedClass')); $this->assertEquals('i18ntestmodule', $collector->findModuleForClass_Test('i18nTest\\i18nTestNamespacedClass')); $this->assertEquals('i18ntestmodule', $collector->findModuleForClass_Test('i18nTestSubModule')); }
/** * Includes all available language files for a certain defined locale. * * @param string $locale All resources from any module in locale $locale will be loaded * @param Boolean $clean Clean old caches? */ public static function include_by_locale($locale, $clean = false) { if ($clean) { self::flush(); } // Get list of module => path pairs, and then just the names $modules = ClassLoader::instance()->getManifest()->getModules(); $moduleNames = array_keys($modules); // Remove the "project" module from the list - we'll add it back specially later if needed global $project; if (($idx = array_search($project, $moduleNames)) !== false) { array_splice($moduleNames, $idx, 1); } // Get the order from the config syste, $order = i18n::config()->get('module_priority'); // Find all modules that don't have their order specified by the config system $unspecified = array_diff($moduleNames, $order); // If the placeholder "other_modules" exists in the order array, replace it by the unspecified modules if (($idx = array_search('other_modules', $order)) !== false) { array_splice($order, $idx, 1, $unspecified); } else { // Otherwise just jam them on the front array_splice($order, 0, 0, $unspecified); } // Put the project module back in at the begining if it wasn't specified by the config system if (!in_array($project, $order)) { array_unshift($order, $project); } $sortedModules = array(); foreach ($order as $module) { if (isset($modules[$module])) { $sortedModules[$module] = $modules[$module]; } } $sortedModules = array_reverse($sortedModules, true); // Loop in reverse order, meaning the translator with the highest priority goes first $translatorsByPrio = array_reverse(self::get_translators(), true); foreach ($translatorsByPrio as $priority => $translators) { /** @var Zend_Translate $translator */ foreach ($translators as $name => $translator) { /** @var i18nTranslateAdapterInterface|Zend_Translate_Adapter $adapter */ $adapter = $translator->getAdapter(); // Load translations from modules foreach ($sortedModules as $module) { $filename = $adapter->getFilenameForLocale($locale); $filepath = "{$module}/lang/" . $filename; if ($filename && !file_exists($filepath)) { continue; } $adapter->addTranslation(array('content' => $filepath, 'locale' => $locale)); } // Load translations from themes // TODO Replace with theme listing once implemented in TemplateManifest $themesBase = Director::baseFolder() . '/themes'; if (is_dir($themesBase)) { foreach (scandir($themesBase) as $theme) { if (strpos($theme, Config::inst()->get('SilverStripe\\View\\SSViewer', 'theme')) === 0 && file_exists("{$themesBase}/{$theme}/lang/")) { $filename = $adapter->getFilenameForLocale($locale); $filepath = "{$themesBase}/{$theme}/lang/" . $filename; if ($filename && !file_exists($filepath)) { continue; } $adapter->addTranslation(array('content' => $filepath, 'locale' => $locale)); } } } // Add empty translations to ensure the locales are "registered" with isAvailable(), // and the next invocation of include_by_locale() doesn't cause a new reparse. $adapter->addTranslation(array('content' => array($locale => $locale), 'locale' => $locale, 'usetranslateadapter' => true)); } } }
/** * Given a backtrace, get the module name from the caller two removed (the caller of the method that called * #notice) * * @param array $backtrace A backtrace as returned from debug_backtrace * @return string The name of the module the call came from, or null if we can't determine */ protected static function get_calling_module_from_trace($backtrace) { if (!isset($backtrace[1]['file'])) { return null; } $callingfile = realpath($backtrace[1]['file']); $manifest = ClassLoader::instance()->getManifest(); foreach ($manifest->getModules() as $name => $path) { if (strpos($callingfile, realpath($path)) === 0) { return $name; } } return null; }
/** * Given a partial class name, attempt to determine the best module to assign strings to. * * @param string $class Either a FQN class name, or a non-qualified class name. * @return string Name of module */ protected function findModuleForClass($class) { if (ClassInfo::exists($class)) { return i18n::get_owner_module($class); } // If we can't find a class, see if it needs to be fully qualified if (strpos($class, '\\') !== false) { return null; } // Find FQN that ends with $class $classes = preg_grep('/' . preg_quote("\\{$class}", '\\/') . '$/i', ClassLoader::instance()->getManifest()->getClassNames()); // Find all modules for candidate classes $modules = array_unique(array_map(function ($class) { return i18n::get_owner_module($class); }, $classes)); if (count($modules) === 1) { return reset($modules); } // Couldn't find it! Exists in none, or multiple modules. return null; }