Skip to content

Instantly share code, notes, and snippets.

@piotrbelina
Created January 10, 2013 15:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save piotrbelina/4502949 to your computer and use it in GitHub Desktop.
Save piotrbelina/4502949 to your computer and use it in GitHub Desktop.
Bootstrap generator for Magento
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* ClassCollectionLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ClassCollectionLoader
{
private static $loaded;
private static $seen;
private static $useTokenizer = true;
/**
* Loads a list of classes and caches them in one big file.
*
* @param array $classes An array of classes to load
* @param string $cacheDir A cache directory
* @param string $name The cache name prefix
* @param Boolean $autoReload Whether to flush the cache when the cache is stale or not
* @param Boolean $adaptive Whether to remove already declared classes or not
* @param string $extension File extension of the resulting file
*
* @throws \InvalidArgumentException When class can't be loaded
*/
public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php')
{
// each $name can only be loaded once per PHP process
if (isset(self::$loaded[$name])) {
return;
}
self::$loaded[$name] = true;
$declared = array_merge(get_declared_classes(), get_declared_interfaces());
if (function_exists('get_declared_traits')) {
$declared = array_merge($declared, get_declared_traits());
}
if ($adaptive) {
// don't include already declared classes
$classes = array_diff($classes, $declared);
// the cache is different depending on which classes are already declared
$name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5);
}
$classes = array_unique($classes);
$cache = $cacheDir.'/'.$name.$extension;
// auto-reload
$reload = false;
if ($autoReload) {
$metadata = $cacheDir.'/'.$name.$extension.'.meta';
if (!is_file($metadata) || !is_file($cache)) {
$reload = true;
} else {
$time = filemtime($cache);
$meta = unserialize(file_get_contents($metadata));
sort($meta[1]);
sort($classes);
if ($meta[1] != $classes) {
$reload = true;
} else {
foreach ($meta[0] as $resource) {
if (!is_file($resource) || filemtime($resource) > $time) {
$reload = true;
break;
}
}
}
}
}
if (!$reload && is_file($cache)) {
require_once $cache;
return;
}
$files = array();
$content = '';
foreach (self::getOrderedClasses($classes) as $class) {
if (in_array($class->getName(), $declared)) {
continue;
}
$files[] = $class->getFileName();
$c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($class->getFileName()));
// add namespace declaration for global code
if (!$class->inNamespace()) {
$c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n";
} else {
$c = self::fixNamespaceDeclarations('<?php '.$c);
$c = preg_replace('/^\s*<\?php/', '', $c);
}
$content .= $c;
}
// cache the core classes
if (!is_dir(dirname($cache))) {
mkdir(dirname($cache), 0777, true);
}
self::writeCacheFile($cache, '<?php '.$content);
if ($autoReload) {
// save the resources
self::writeCacheFile($metadata, serialize(array($files, $classes)));
}
}
/**
* Adds brackets around each namespace if it's not already the case.
*
* @param string $source Namespace string
*
* @return string Namespaces with brackets
*/
public static function fixNamespaceDeclarations($source)
{
if (!function_exists('token_get_all') || !self::$useTokenizer) {
if (preg_match('/namespace(.*?)\s*;/', $source)) {
$source = preg_replace('/namespace(.*?)\s*;/', "namespace$1\n{", $source)."}\n";
}
return $source;
}
$output = '';
$inNamespace = false;
$tokens = token_get_all($source);
for ($i = 0, $max = count($tokens); $i < $max; $i++) {
$token = $tokens[$i];
if (is_string($token)) {
$output .= $token;
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
// strip comments
continue;
} elseif (T_NAMESPACE === $token[0]) {
if ($inNamespace) {
$output .= "}\n";
}
$output .= $token[1];
// namespace name and whitespaces
while (($t = $tokens[++$i]) && is_array($t) && in_array($t[0], array(T_WHITESPACE, T_NS_SEPARATOR, T_STRING))) {
$output .= $t[1];
}
if (is_string($t) && '{' === $t) {
$inNamespace = false;
--$i;
} else {
$output = rtrim($output);
$output .= "\n{";
$inNamespace = true;
}
} else {
$output .= $token[1];
}
}
if ($inNamespace) {
$output .= "}\n";
}
return $output;
}
/**
* Writes a cache file.
*
* @param string $file Filename
* @param string $content Temporary file content
*
* @throws \RuntimeException when a cache file cannot be written
*/
private static function writeCacheFile($file, $content)
{
$tmpFile = tempnam(dirname($file), basename($file));
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
@chmod($file, 0666 & ~umask());
return;
}
throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file));
}
/**
* Removes comments from a PHP source string.
*
* We don't use the PHP php_strip_whitespace() function
* as we want the content to be readable and well-formatted.
*
* @param string $source A PHP string
*
* @return string The PHP string with the comments removed
*/
private static function stripComments($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= $token[1];
}
}
// replace multiple new lines with a single newline
$output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
return $output;
}
/**
* Gets an ordered array of passed classes including all their dependencies.
*
* @param array $classes
*
* @return array An array of sorted \ReflectionClass instances (dependencies added if needed)
*
* @throws \InvalidArgumentException When a class can't be loaded
*/
private static function getOrderedClasses(array $classes)
{
$map = array();
self::$seen = array();
foreach ($classes as $class) {
try {
$reflectionClass = new \ReflectionClass($class);
} catch (\ReflectionException $e) {
throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
}
$map = array_merge($map, self::getClassHierarchy($reflectionClass));
}
return $map;
}
private static function getClassHierarchy(ReflectionClass $class)
{
if (isset(self::$seen[$class->getName()])) {
return array();
}
self::$seen[$class->getName()] = true;
$classes = array($class);
$parent = $class;
while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) {
self::$seen[$parent->getName()] = true;
array_unshift($classes, $parent);
}
if (function_exists('get_declared_traits')) {
foreach ($classes as $c) {
foreach (self::getTraits($c) as $trait) {
self::$seen[$trait->getName()] = true;
array_unshift($classes, $trait);
}
}
}
return array_merge(self::getInterfaces($class), $classes);
}
private static function getInterfaces(ReflectionClass $class)
{
$classes = array();
foreach ($class->getInterfaces() as $interface) {
$classes = array_merge($classes, self::getInterfaces($interface));
}
if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) {
self::$seen[$class->getName()] = true;
$classes[] = $class;
}
return $classes;
}
private static function getTraits(ReflectionClass $class)
{
$traits = $class->getTraits();
$classes = array();
while ($trait = array_pop($traits)) {
if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) {
$classes[] = $trait;
$traits = array_merge($traits, $trait->getTraits());
}
}
return $classes;
}
/**
* This method is only useful for testing.
*/
public static function enableTokenizer($bool)
{
self::$useTokenizer = (Boolean) $bool;
}
}
function doBuildBootstrap($appDir)
{
$file = $appDir.'/bootstrap.php.cache';
if (file_exists($file)) {
unlink($file);
}
ClassCollectionLoader::load(array(
164 => 'Varien_Profiler',
165 => 'Mage_Core_Model_App',
166 => 'Varien_Event_Collection',
167 => 'Varien_Event_Observer_Collection',
168 => 'Varien_Simplexml_Config',
169 => 'Mage_Core_Model_Config_Base',
170 => 'Mage_Core_Model_Config',
171 => 'Mage_Core_Model_Locale',
172 => 'Varien_Simplexml_Element',
173 => 'Mage_Core_Model_Config_Element',
174 => 'Mage_Core_Model_Cache',
175 => 'Zend_Cache',
176 => 'Zend_Cache_Backend',
177 => 'Zend_Cache_Core',
178 => 'Varien_Cache_Core',
179 => 'Mage_Core_Model_App_Area',
180 => 'Mage_Core_Model_Resource_Abstract',
181 => 'Mage_Core_Model_Mysql4_Abstract',
182 => 'Mage_Core_Model_Mysql4_Website',
183 => 'Mage_Core_Model_Resource',
184 => 'Mage_Core_Model_Resource_Type_Abstract',
185 => 'Mage_Core_Model_Resource_Type_Db',
186 => 'Mage_Core_Model_Resource_Type_Db_Pdo_Mysql',
187 => 'Zend_Db_Adapter_Abstract',
188 => 'Zend_Db_Adapter_Pdo_Abstract',
189 => 'Zend_Db_Adapter_Pdo_Mysql',
190 => 'Varien_Db_Adapter_Pdo_Mysql',
191 => 'Zend_Db',
192 => 'Zend_Db_Select',
193 => 'Varien_Db_Select',
194 => 'Varien_Object',
195 => 'Mage_Core_Model_Config_Options',
196 => 'Mage_Core_Model_Abstract',
197 => 'Mage_Core_Model_Website',
198 => 'Varien_Data_Collection',
199 => 'Varien_Data_Collection_Db',
200 => 'Mage_Core_Model_Mysql4_Collection_Abstract',
201 => 'Mage_Core_Model_Mysql4_Website_Collection',
202 => 'Zend_Db_Statement',
203 => 'Zend_Db_Statement_Pdo',
205 => 'Enterprise_PageCache_Model_Processor',
207 => 'Mage_Core_Model_Store_Group',
208 => 'Mage_Core_Model_Mysql4_Store_Group',
209 => 'Mage_Core_Model_Mysql4_Store_Group_Collection',
210 => 'Mage_Core_Model_Store',
211 => 'Mage_Core_Model_Mysql4_Store',
212 => 'Mage_Core_Model_Mysql4_Store_Collection',
213 => 'Zend_Db_Expr',
214 => 'Mage_Core_Model_Cookie',
216 => 'Zend_Controller_Request_Abstract',
217 => 'Zend_Controller_Request_Http',
218 => 'Mage_Core_Controller_Request_Http',
219 => 'Mage_Core_Model_Resource_Setup',
220 => 'Mage_Core_Controller_Varien_Front',
221 => 'Varien_Event',
222 => 'Varien_Event_Observer',
223 => 'Enterprise_Staging_Model_Observer',
224 => 'Magentix_RewritingFilters_Model_Observer',
225 => 'Mage_Core_Controller_Varien_Router_Abstract',
226 => 'Mage_Core_Controller_Varien_Router_Standard',
227 => 'Mage_Core_Controller_Varien_Router_Admin',
228 => 'Mage_Cms_Controller_Router',
229 => 'Magentix_RewritingFilters_Controller_Router',
230 => 'Mage_Core_Controller_Varien_Router_Default',
231 => 'Mage_Core_Model_Url_Rewrite',
232 => 'Zend_Controller_Response_Abstract',
233 => 'Zend_Controller_Response_Http',
234 => 'Mage_Core_Controller_Response_Http',
235 => 'Mage_Core_Model_Mysql4_Url_Rewrite',
236 => 'Mage_Core_Controller_Varien_Action',
237 => 'Mage_Core_Controller_Front_Action',
239 => 'Mage_Core_Model_Layout',
240 => 'Mage_Core_Model_Layout_Element',
241 => 'Mage_Core_Model_Layout_Update',
242 => 'Mage_Core_Model_Session_Abstract_Varien',
243 => 'Mage_Core_Model_Session_Abstract',
244 => 'Mage_Core_Model_Session',
245 => 'Mage_Core_Model_Message_Collection',
246 => 'Mage_Core_Helper_Abstract',
247 => 'Mage_Core_Helper_Http',
248 => 'Mage_Core_Model_Design_Package',
249 => 'Fooman_SpeedsterEnterprise_Model_Core_Design_Package',
250 => 'Mage_Core_Model_Design',
251 => 'Mage_Core_Model_Mysql4_Design',
252 => 'Varien_Exception',
253 => 'Mage_Core_Model_Translate',
254 => 'Mage_Core_Model_Translate_Inline',
256 => 'Mage_Log_Model_Visitor',
257 => 'Mage_Core_Helper_String',
258 => 'Mage_Log_Model_Mysql4_Visitor',
259 => 'Enterprise_WebsiteRestriction_Model_Observer',
261 => 'Enterprise_PageCache_Model_Observer',
263 => 'Enterprise_PageCache_Model_Config',
270 => 'Mage_Page_Helper_Layout',
271 => 'Mage_Page_Model_Config',
272 => 'Mage_Page_Helper_Data',
273 => 'Mage_Core_Model_Translate_Expr',
295 => 'Mage_Core_Block_Abstract',
296 => 'Mage_Core_Block_Template',
297 => 'Mage_Page_Block_Html',
298 => 'Mage_Page_Block_Html_Head',
), dirname($file), basename($file, '.php.cache'), false, false, '.php.cache');
file_put_contents($file, sprintf("<?php
//namespace { \$loader = require_once __DIR__.'/autoload.php'; }
%s
//namespace { return \$loader; }
", substr(file_get_contents($file), 5)));
}
require_once __DIR__ . '/../lib/Varien/Autoload.php';
define('DS', DIRECTORY_SEPARATOR);
define('PS', PATH_SEPARATOR);
define('BP', dirname(dirname(__FILE__)));
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'local';
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'community';
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'core';
$paths[] = BP . DS . 'lib';
$appPath = implode(PS, $paths);
set_include_path($appPath . PS . get_include_path());
Varien_Autoload::register();
doBuildBootstrap(__DIR__ . '/../app/');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment