Доброго времени суток, дорогие читатели!
Этим постом я начинаю цикл разбора компонентов Symfony2
И первым мне на пути попался Composer, а именно его реализация автозагрузчика
Если вам когда-нибудь было интересно как он работает, как хранит свои зависимости добро пожаловать под кат
Не будем долго разглагольствовать, пора приниматься за дело:
1. Первым делом Symfony2 загружает app/bootstrap.php.cache (в этом файле хранятся самые востребованные классы ядра symfony. такие как Response, Request, Kernel…)
2. Первой же строчкой bootstrap поддягивает файл app/autoload.php
3. И это еще не конец, теперь мы вызываем файл, который лежит у нас в папке vendors, а именно vendors/autoload.php
4. Т.к. авторы компонентов не лишены чувства юмора, то теперь мы подгружаем файл autoload_real.php, который находится в папке composer (мы все ближе и ближе к нашей цели)
5. И наконец, то мы вызываем наше первую функцию (из файла vendors/autoload.php, сразу после загрузки “реального” autoload). Эта функция вызывает статический метод из класса, который находился в autoload_real, как вы сможете заметить, при изменениях в composer он меняется, у меня он называется ComposerAutoloaderInit8bc6390ef3aa110c65c0080e10044294, вот такое незамысловатое название. Из этого файла нам нужна статический метод getLoader, который в конце должен вернуть нам наконец-то таки загрузчик.
6. И так, приступим к разбору кода этой функции:
1 2 3 |
if (null !== self::$loader) { return self::$loader; } |
Как не трудно догадаться, здесь код пытается найти готовый loader, чтобы ничего не делать(вот лентяй), но это у него не выходит и он вынужден выполняться дальше 🙂
7. “супер реальный” autoloader
1 2 3 |
spl_autoload_register(array('ComposerAutoloaderInit8bc6390ef3aa110c65c0080e10044294', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInit8bc6390ef3aa110c65c0080e10044294', 'loadClassLoader')); |
Если где и есть real autoload, то вот он ClassLoader, именно за ним мы так долго и добирались
7.1 Первая строчка сначала регистрирует loadClassLoader, который находится в том же классе где и мы, как загрузчик, с одной единственной целью – загружить ClassLoader
7.3 В третье строчке этот autoloader, удаляется
8. А сейчас начинается целая эпопея, в ходе которой загружаются все карты, записанные в виде PSR-0, PSR-4 и бог знает как еще
8.1 Заполняем prefixesPsr0
1 2 3 4 |
$map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } |
И так, вначале мы загружаем все namespaces
Причем в начем они будут храниться в массиве prefixesPsr0
При чем в виде
1 2 3 4 5 6 7 |
prefixesPsr0 = array ( "T" => array ( "Twig_Extensions_" => путь_к_twig_extension_lib, ... } ... } |
И запишется путь к нашим сорцам
1 |
fallbackDirsPsr0 => путь_к_папке_src |
8.2 Заполнение массивов prefixLengthsPsr4 & prefixDirsPsr4
1 2 3 4 |
$map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } |
Ситуация примерно такая же, как и вредыдущем пункте, но заполняется уже 2 массива (зачем это нужно было, расскажу потом, когда сам пойму)
8.3 Переходим к неменее захватывающему заполнению массива
1 2 3 4 |
$classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } |
Благо он заполняется с первого раза, даже без прохода по циклу
9. И вот настал этот час, мы регистрируем наш loader, наш ненаглядный, долгожданный ClassLoader ура товарищи, ура!
1 |
spl_autoload_register(array($this, 'loadClass'), true, $prepend); |
А в качестве autoload у нас будет vendors/Composer/Autoload/ClassLoader::loadClass
Вот на этот метод, если вы будете дебажить свои код, вы будуте натыкаться довольно часто
Он до безобразия прост
1 2 3 4 |
if ($file = $this->findFile($class)) { includeFile($file); return true; } |
No comment
Вот ради вот этого вот… с позволения сказать метода мы проделали весь этот путь. Как говориться “все гениальное – просто”
Как Вы понимаете, самое интересное находится в методе findFile, а точнее findFileWithExtension, здесь идет разбор всех тех массивов, что мы наворатили в пункте 8 обратно в пути к папкам и файлам
10. Дальше уже не интересно, мы просто возвращаем наш объект $loader, которым и будем пользоваться в дальнейшим
За сим завершаю