Changeset View
Changeset View
Standalone View
Standalone View
core/kernel/utility/ClassDiscovery/ClassMapBuilder.php
Show All 19 Lines | |||||
use PhpParser\NodeVisitor\NameResolver; | use PhpParser\NodeVisitor\NameResolver; | ||||
use PhpParser\Parser; | use PhpParser\Parser; | ||||
defined('FULL_PATH') or die('restricted access!'); | defined('FULL_PATH') or die('restricted access!'); | ||||
class ClassMapBuilder | class ClassMapBuilder | ||||
{ | { | ||||
const CACHE_FORMAT = 1; | const CACHE_FORMAT = 2; | ||||
const CACHE_FILE_STRUCTURE = 'class_structure.php'; | const CACHE_FILE_STRUCTURE = 'class_structure.php'; | ||||
const CACHE_FILE_HASHES = 'file_hashes.php'; | const CACHE_FILE_HASHES = 'file_hashes.php'; | ||||
/** | /** | ||||
* Path to scan. | * Path to scan. | ||||
* | * | ||||
Show All 11 Lines | |||||
/** | /** | ||||
* List of classes (key - class, value - file). | * List of classes (key - class, value - file). | ||||
* | * | ||||
* @var array | * @var array | ||||
*/ | */ | ||||
protected $classToFileMap = array(); | protected $classToFileMap = array(); | ||||
/** | /** | ||||
* List of classes, declared in each file (key - file, value - class list). | * Class information used during cache building (file > class > class_info). | ||||
* | * | ||||
* @var array | * @var array | ||||
*/ | */ | ||||
protected $fileToClassMap = array(); | protected $buildingCache = array(); | ||||
/** | |||||
* Class information (type, extends, implements, etc.). | |||||
* | |||||
* @var array | |||||
*/ | |||||
protected $classInfo = array(); | |||||
/** | /** | ||||
* Stores hash of each file on given path. | * Stores hash of each file on given path. | ||||
* | * | ||||
* @var array | * @var array | ||||
*/ | */ | ||||
protected $fileHashes = array(); | protected $fileHashes = array(); | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Line(s) | |||||
protected function assertPath($path) | protected function assertPath($path) | ||||
{ | { | ||||
if ( !file_exists($path) || !is_dir($path) ) { | if ( !file_exists($path) || !is_dir($path) ) { | ||||
throw new \InvalidArgumentException('Path "' . $path . '" is not a folder or doesn\'t exist'); | throw new \InvalidArgumentException('Path "' . $path . '" is not a folder or doesn\'t exist'); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Returns class map, that was build previously. | * Returns class map and class information, that was built previously. | ||||
* | * | ||||
* @return array | * @return array | ||||
*/ | */ | ||||
public function get() | public function get() | ||||
{ | { | ||||
$this->load(self::CACHE_FILE_STRUCTURE, false); | $this->load(self::CACHE_FILE_STRUCTURE, false); | ||||
return $this->classToFileMap; | return array($this->classToFileMap, $this->classInfo); | ||||
} | } | ||||
/** | /** | ||||
* Builds class map. | * Builds class map. | ||||
* | * | ||||
* @return void | * @return void | ||||
* @throws \RuntimeException When PHP parser not found. | * @throws \RuntimeException When PHP parser not found. | ||||
*/ | */ | ||||
public function build() | public function build() | ||||
{ | { | ||||
if ( !class_exists('PhpParser\Parser') ) { | if ( !class_exists('PhpParser\Parser') ) { | ||||
$error_msg = 'PHP Parser not found. '; | $error_msg = 'PHP Parser not found. Make sure, that Composer dependencies were '; | ||||
$error_msg .= 'Make sure, that Composer dependencies were installed using "php composer.phar install --dev" command.'; | $error_msg .= 'installed using "php composer.phar install --dev" command.'; | ||||
throw new \RuntimeException($error_msg); | throw new \RuntimeException($error_msg); | ||||
} | } | ||||
$scan_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '...', $this->scanPath, 1); | $scan_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '...', $this->scanPath, 1); | ||||
echo $this->strPad('path "' . $scan_path . '"', 40); | echo $this->strPad('path "' . $scan_path . '"', 40); | ||||
$this->load(self::CACHE_FILE_STRUCTURE, true); | $this->load(self::CACHE_FILE_STRUCTURE, true); | ||||
Show All 11 Lines | |||||
$this->parseFile($file); | $this->parseFile($file); | ||||
} | } | ||||
echo $this->strPad('parsed in ' . sprintf('%.4f', microtime(true) - $start) . 's', 25); | echo $this->strPad('parsed in ' . sprintf('%.4f', microtime(true) - $start) . 's', 25); | ||||
echo PHP_EOL; | echo PHP_EOL; | ||||
ksort($this->classToFileMap); | ksort($this->classToFileMap); | ||||
ksort($this->fileHashes); | ksort($this->fileHashes); | ||||
ksort($this->classInfo); | |||||
$this->store(self::CACHE_FILE_STRUCTURE); | $this->store(self::CACHE_FILE_STRUCTURE); | ||||
$this->store(self::CACHE_FILE_HASHES); | $this->store(self::CACHE_FILE_HASHES); | ||||
} | } | ||||
/** | /** | ||||
* Pads text with spaces from right side. | * Pads text with spaces from right side. | ||||
* | * | ||||
Show All 20 Lines | |||||
$file_path = $this->getCacheFilename($filename); | $file_path = $this->getCacheFilename($filename); | ||||
if ( !file_exists($file_path) ) { | if ( !file_exists($file_path) ) { | ||||
return; | return; | ||||
} | } | ||||
$cache = include $file_path; | $cache = include $file_path; | ||||
if ( $cache['cache_format'] < self::CACHE_FORMAT ) { | if ( $cache['cache_format'] != self::CACHE_FORMAT ) { | ||||
return; | return; | ||||
} | } | ||||
if ( $filename === self::CACHE_FILE_STRUCTURE ) { | if ( $filename === self::CACHE_FILE_STRUCTURE ) { | ||||
$class_info = $cache['class_info']; | |||||
if ( $for_writing ) { | if ( $for_writing ) { | ||||
foreach ( $cache['classes'] as $class => $file ) { | foreach ( $cache['classes'] as $class => $file ) { | ||||
if ( !isset($this->fileToClassMap[$file]) ) { | if ( !isset($this->buildingCache[$file]) ) { | ||||
$this->fileToClassMap[$file] = array(); | $this->buildingCache[$file] = array(); | ||||
} | } | ||||
$this->fileToClassMap[$file][] = $class; | $this->buildingCache[$file][$class] = $class_info[$class]; | ||||
} | } | ||||
} | } | ||||
else { | else { | ||||
$this->classToFileMap = $cache['classes']; | $this->classToFileMap = $cache['classes']; | ||||
$this->classInfo = $class_info; | |||||
} | } | ||||
} | } | ||||
elseif ( $filename === self::CACHE_FILE_HASHES ) { | elseif ( $filename === self::CACHE_FILE_HASHES ) { | ||||
$this->fileHashes = $cache['file_hashes']; | $this->fileHashes = $cache['file_hashes']; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Line(s) | |||||
$this->currentFile = $file; | $this->currentFile = $file; | ||||
$code = file_get_contents(FULL_PATH . $file); | $code = file_get_contents(FULL_PATH . $file); | ||||
$current_hash = filesize(FULL_PATH . $file); | $current_hash = filesize(FULL_PATH . $file); | ||||
$previous_hash = isset($this->fileHashes[$file]) ? $this->fileHashes[$file] : 0; | $previous_hash = isset($this->fileHashes[$file]) ? $this->fileHashes[$file] : 0; | ||||
if ( $current_hash === $previous_hash ) { | if ( $current_hash === $previous_hash ) { | ||||
// File wasn't change since time, when cache was built. | // File wasn't change since time, when cache was built. | ||||
if ( isset($this->fileToClassMap[$file]) ) { | if ( isset($this->buildingCache[$file]) ) { | ||||
foreach ( $this->fileToClassMap[$file] as $class ) { | foreach ( $this->buildingCache[$file] as $class => $class_info ) { | ||||
$this->addClass($class); | $this->addClass($class, $class_info); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
else { | else { | ||||
// Parse file, because it's content doesn't match the cache. | // Parse file, because it's content doesn't match the cache. | ||||
$this->fileToClassMap[$file] = array(); | |||||
$this->fileHashes[$file] = $current_hash; | $this->fileHashes[$file] = $current_hash; | ||||
$statements = $this->parser->parse($code); | $statements = $this->parser->parse($code); | ||||
$this->traverser->traverse($statements); | $this->traverser->traverse($statements); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Stores cache to disk. | * Stores cache to disk. | ||||
* | * | ||||
* @param string $filename Cache filename. | * @param string $filename Cache filename. | ||||
* | * | ||||
* @return void | * @return void | ||||
* @throws \RuntimeException When cache could not be written. | * @throws \RuntimeException When cache could not be written. | ||||
*/ | */ | ||||
protected function store($filename) | protected function store($filename) | ||||
{ | { | ||||
$cache = array('cache_format' => self::CACHE_FORMAT); | $cache = array('cache_format' => self::CACHE_FORMAT); | ||||
if ( $filename === self::CACHE_FILE_STRUCTURE ) { | if ( $filename === self::CACHE_FILE_STRUCTURE ) { | ||||
$cache['classes'] = $this->classToFileMap; | $cache['classes'] = $this->classToFileMap; | ||||
$cache['class_info'] = $this->classInfo; | |||||
} | } | ||||
elseif ( $filename === self::CACHE_FILE_HASHES ) { | elseif ( $filename === self::CACHE_FILE_HASHES ) { | ||||
$cache['file_hashes'] = $this->fileHashes; | $cache['file_hashes'] = $this->fileHashes; | ||||
} | } | ||||
$cache = $this->prettyVarExport($cache); | $cache = $this->prettyVarExport($cache); | ||||
$at = '@'; | $at = '@'; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Line(s) | |||||
protected function getCacheFilename($filename) | protected function getCacheFilename($filename) | ||||
{ | { | ||||
return $this->cachePath . '/' . $filename; | return $this->cachePath . '/' . $filename; | ||||
} | } | ||||
/** | /** | ||||
* Adds class to the map. | * Adds class to the map. | ||||
* | * | ||||
* @param string $class Class. | * @param string $class Class. | ||||
* @param array $class_info Class info. | |||||
* | * | ||||
* @return void | * @return void | ||||
*/ | */ | ||||
public function addClass($class) | public function addClass($class, array $class_info) | ||||
{ | { | ||||
$this->classInfo[$class] = $class_info; | |||||
$this->classToFileMap[$class] = $this->currentFile; | $this->classToFileMap[$class] = $this->currentFile; | ||||
$this->fileToClassMap[$this->currentFile][] = $class; | |||||
} | } | ||||
} | } |