| Current Path : /var/www/surf/TYPO3/vendor/typo3/cms-core/Classes/Core/ |
| Current File : /var/www/surf/TYPO3/vendor/typo3/cms-core/Classes/Core/ClassLoadingInformationGenerator.php |
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Core;
use Composer\Autoload\ClassLoader;
use Composer\Autoload\ClassMapGenerator;
use TYPO3\CMS\Core\Error\Exception;
use TYPO3\CMS\Core\Package\PackageInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Generates class loading information (class maps, class aliases etc.) and writes it to files
* for further inclusion in the bootstrap
* @internal
*/
class ClassLoadingInformationGenerator
{
/**
* @var PackageInterface[]
*/
protected $activeExtensionPackages;
/**
* @var ClassLoader
*/
protected $classLoader;
/**
* @var string
*/
protected $installationRoot;
/**
* @var bool
*/
protected $isDevMode;
/**
* @param string $installationRoot
* @param bool $isDevMode
*/
public function __construct(ClassLoader $classLoader, array $activeExtensionPackages, $installationRoot, $isDevMode = false)
{
$this->classLoader = $classLoader;
$this->activeExtensionPackages = $activeExtensionPackages;
$this->installationRoot = $installationRoot;
$this->isDevMode = $isDevMode;
}
/**
* Returns class loading information for a single package
*
* @param PackageInterface $package The package to generate the class loading info for
* @param bool $useRelativePaths If set to TRUE, make the path relative to the current TYPO3 public web path
* @return array
*/
public function buildClassLoadingInformationForPackage(PackageInterface $package, $useRelativePaths = false)
{
$classMap = [];
$psr4 = [];
$packagePath = $package->getPackagePath();
$manifest = $package->getValueFromComposerManifest();
if (empty($manifest->autoload)) {
// Legacy mode: Scan the complete extension directory for class files
$classMap = $this->createClassMap($packagePath, $useRelativePaths, !$this->isDevMode);
} else {
$autoloadPsr4 = $this->getAutoloadSectionFromManifest($manifest, 'psr-4');
if (!empty($autoloadPsr4)) {
foreach ($autoloadPsr4 as $namespacePrefix => $paths) {
foreach ((array)$paths as $path) {
$namespacePath = $packagePath . $path;
$namespaceRealPath = (string)realpath($namespacePath);
if ($useRelativePaths) {
$psr4[$namespacePrefix][] = $this->makePathRelative($namespacePath, $namespaceRealPath);
} else {
$psr4[$namespacePrefix][] = $namespacePath;
}
if (!empty($namespaceRealPath) && is_dir($namespaceRealPath)) {
// Add all prs-4 classes to the class map for improved class loading performance
$classMap = array_merge($classMap, $this->createClassMap($namespacePath, $useRelativePaths, false, $namespacePrefix));
}
}
}
}
$autoloadClassmap = $this->getAutoloadSectionFromManifest($manifest, 'classmap');
if (!empty($autoloadClassmap)) {
foreach ($autoloadClassmap as $path) {
$classMap = array_merge($classMap, $this->createClassMap($packagePath . $path, $useRelativePaths));
}
}
}
return ['classMap' => $classMap, 'psr-4' => $psr4];
}
/**
* Fetches class loading info from the according section from the manifest file.
* Development information will be extracted and merged as well.
*
* @param \stdClass $manifest
* @param string $section
* @return array<string,array>
*/
protected function getAutoloadSectionFromManifest($manifest, $section)
{
$finalAutoloadSection = [];
$autoloadDefinition = json_decode((string)json_encode($manifest->autoload), true);
if (!empty($autoloadDefinition[$section]) && is_array($autoloadDefinition[$section])) {
$finalAutoloadSection = $autoloadDefinition[$section];
}
if ($this->isDevMode) {
if (isset($manifest->{'autoload-dev'})) {
$autoloadDefinitionDev = json_decode((string)json_encode($manifest->{'autoload-dev'}), true);
if (!empty($autoloadDefinitionDev[$section]) && is_array($autoloadDefinitionDev[$section])) {
$finalAutoloadSection = array_merge($finalAutoloadSection, $autoloadDefinitionDev[$section]);
}
}
}
return $finalAutoloadSection;
}
/**
* Creates a class map for a given (absolute) path
*
* @param string $classesPath
* @param bool $useRelativePaths
* @param bool $ignorePotentialTestClasses
* @param string $namespace
* @return array
*/
protected function createClassMap($classesPath, $useRelativePaths = false, $ignorePotentialTestClasses = false, $namespace = null)
{
$classMap = [];
$blacklistExpression = null;
if ($ignorePotentialTestClasses) {
$blacklistPathPrefix = (string)realpath($classesPath);
$blacklistPathPrefix = str_replace('\\', '/', $blacklistPathPrefix);
$blacklistExpression = "{($blacklistPathPrefix/tests/|$blacklistPathPrefix/Tests/|$blacklistPathPrefix/Resources/|$blacklistPathPrefix/res/|$blacklistPathPrefix/class.ext_update.php)}";
}
foreach (ClassMapGenerator::createMap($classesPath, $blacklistExpression, null, $namespace) as $class => $path) {
if ($useRelativePaths) {
$classMap[$class] = $this->makePathRelative($classesPath, $path);
} else {
$classMap[$class] = $path;
}
}
return $classMap;
}
/**
* Returns class alias map for given package
*
* @param PackageInterface $package The package to generate the class alias info for
* @throws \TYPO3\CMS\Core\Error\Exception
* @return array
*/
public function buildClassAliasMapForPackage(PackageInterface $package)
{
$aliasToClassNameMapping = [];
$classNameToAliasMapping = [];
$possibleClassAliasFiles = [];
$manifest = $package->getValueFromComposerManifest();
if (!empty($manifest->extra->{'typo3/class-alias-loader'}->{'class-alias-maps'})) {
$possibleClassAliasFiles = $manifest->extra->{'typo3/class-alias-loader'}->{'class-alias-maps'};
if (!is_array($possibleClassAliasFiles)) {
throw new Exception('"typo3/class-alias-loader"/"class-alias-maps" must return an array!', 1444142481);
}
} else {
$possibleClassAliasFiles[] = 'Migrations/Code/ClassAliasMap.php';
}
$packagePath = $package->getPackagePath();
foreach ($possibleClassAliasFiles as $possibleClassAliasFile) {
$possiblePathToClassAliasFile = $packagePath . $possibleClassAliasFile;
if (file_exists($possiblePathToClassAliasFile)) {
$packageAliasMap = require $possiblePathToClassAliasFile;
if (!is_array($packageAliasMap)) {
throw new Exception('"class alias maps" must return an array', 1422625075);
}
foreach ($packageAliasMap as $aliasClassName => $className) {
$lowerCasedAliasClassName = strtolower($aliasClassName);
$aliasToClassNameMapping[$lowerCasedAliasClassName] = $className;
$classNameToAliasMapping[$className][$lowerCasedAliasClassName] = $lowerCasedAliasClassName;
}
}
}
return ['aliasToClassNameMapping' => $aliasToClassNameMapping, 'classNameToAliasMapping' => $classNameToAliasMapping];
}
/**
* Generate the class map file
* @return string[]
* @internal
*/
public function buildAutoloadInformationFiles()
{
$psr4File = $classMapFile = <<<EOF
<?php
// autoload_classmap.php @generated by TYPO3
\$typo3InstallDir = \TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/';
return array(
EOF;
$classMap = [];
$psr4 = [];
foreach ($this->activeExtensionPackages as $package) {
$classLoadingInformation = $this->buildClassLoadingInformationForPackage($package, true);
$classMap = array_merge($classMap, $classLoadingInformation['classMap']);
$psr4 = array_merge($psr4, $classLoadingInformation['psr-4']);
}
ksort($classMap);
ksort($psr4);
foreach ($classMap as $class => $relativePath) {
$classMapFile .= sprintf(' %s => %s,', var_export($class, true), $this->getPathCode($relativePath)) . "\n";
}
$classMapFile .= ");\n";
foreach ($psr4 as $prefix => $relativePaths) {
$psr4File .= sprintf(' %s => array(%s),', var_export($prefix, true), implode(',', array_map([$this, 'getPathCode'], $relativePaths))) . "\n";
}
$psr4File .= ");\n";
return ['classMapFile' => $classMapFile, 'psr-4File' => $psr4File];
}
/**
* Generate a relative path string from an absolute path within a give package path
*
* @param string $packagePath
* @param string $realPathOfClassFile
* @param bool $relativeToRoot
* @return string
*/
protected function makePathRelative($packagePath, $realPathOfClassFile, $relativeToRoot = true)
{
$realPathOfClassFile = GeneralUtility::fixWindowsFilePath((string)$realPathOfClassFile);
$packageRealPath = GeneralUtility::fixWindowsFilePath((string)realpath($packagePath));
$relativePackagePath = rtrim(substr($packagePath, strlen($this->installationRoot)), '/');
if ($relativeToRoot) {
if ($realPathOfClassFile === $packageRealPath) {
$relativePathToClassFile = $relativePackagePath;
} else {
$relativePathToClassFile = $relativePackagePath . '/' . ltrim(substr($realPathOfClassFile, strlen($packageRealPath)), '/');
}
} else {
$relativePathToClassFile = ltrim(substr($realPathOfClassFile, strlen($packageRealPath)), '/');
}
return $relativePathToClassFile;
}
/**
* Generate a relative path string from a relative path
*
* @param string $relativePathToClassFile
* @return string
*/
protected function getPathCode($relativePathToClassFile)
{
return '$typo3InstallDir . ' . var_export($relativePathToClassFile, true);
}
/**
* Build class alias mapping file
*
* @return string
* @throws \Exception
* @internal
*/
public function buildClassAliasMapFile()
{
$aliasToClassNameMapping = [];
$classNameToAliasMapping = [];
foreach ($this->activeExtensionPackages as $package) {
$aliasMappingForPackage = $this->buildClassAliasMapForPackage($package);
$aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $aliasMappingForPackage['aliasToClassNameMapping']);
$classNameToAliasMapping = array_merge($classNameToAliasMapping, $aliasMappingForPackage['classNameToAliasMapping']);
}
$exportArray = [
'aliasToClassNameMapping' => $aliasToClassNameMapping,
'classNameToAliasMapping' => $classNameToAliasMapping,
];
$fileContent = "<?php\nreturn ";
$fileContent .= var_export($exportArray, true);
$fileContent .= ";\n";
return $fileContent;
}
}