ConLite/conlite/tools/mpAutoloaderClassMap/mpClassTypeFinder.php

401 Zeilen
12 KiB
PHP

<?php
/**
* Contains class type token finder.
*
* @category Development
* @package mpAutoloaderClassMap
* @author Murat Purc <murat@purc.de>
* @copyright Copyright (c) 2009-2010 Murat Purc (http://www.purc.de)
* @license http://www.gnu.org/licenses/gpl-2.0.html - GNU General Public License, version 2
* @version $Id$
*/
/**
* Class to find class type tokens
*
* @category Development
* @package mpAutoloaderClassMap
* @author Murat Purc <murat@purc.de>
*/
class mpClassTypeFinder
{
/**
* List of directories to ignore (note: is case insensitive)
* @var array
*/
protected $_excludeDirs = array('.svn', '.cvs');
/**
* List of files to ignore, regex pattern is also accepted (note: is case insensitive)
* @var array
*/
protected $_excludeFiles = array('/^~*.\.php$/', '/^~*.\.inc$/');
/**
* List of file extensions to parse (note: is case insensitive)
* @var array
*/
protected $_extensionsToParse = array('.php', '.inc');
/**
* Flag to enable debugging, all messages will be collected in property _debugMessages,
* if enabled
* @var bool
*/
protected $_enableDebug = false;
/**
* List of debugging messages, will e filled, if debugging is active
* @var array
*/
protected $_debugMessages = array();
/**
* Initializes class with passed options
*
* @param array $options Assoziative options array as follows:
* - excludeDirs: (array) List of directories to exclude, optional.
* Default values are '.svn' and '.cvs'.
* - excludeFiles: (array) List of files to exclude, optional.
* Default values are '/^~*.\.php$/' and '/^~*.\.inc$/'.
* - extensionsToParse: (array) List of file extensions to parse, optional.
* Default values are '.php' and '.inc'.
* - enableDebug: (bool) Flag to enable debugging, optional.
* Default value is false.
*/
public function __construct(array $options=array())
{
if (isset($options['excludeDirs']) && is_array($options['excludeDirs'])) {
$this->setExcludeDirs($options['excludeDirs']);
}
if (isset($options['excludeFiles']) && is_array($options['excludeFiles'])) {
$this->setExcludeFiles($options['excludeFiles']);
}
if (isset($options['extensionsToParse']) && is_array($options['extensionsToParse'])) {
$this->setExtensionsToParse($options['extensionsToParse']);
}
if (isset($options['enableDebug']) && is_bool($options['enableDebug'])) {
$this->_enableDebug = $options['enableDebug'];
}
}
/**
* Sets directories to exclude
*
* @param array $excludeDirs
* @return void
*/
public function setExcludeDirs(array $excludeDirs)
{
$this->_excludeDirs = $excludeDirs;
}
/**
* Returns list of directories to exclude
*
* @return array
*/
public function getExcludeDirs()
{
return $this->_excludeDirs;
}
/**
* Sets files to exclude
*
* @param array $excludeFiles Feasible values are
* - temp.php (single file name)
* - ~*.php (with * wildcard)
* Will be replaced against regex '/^~.*\.php$/'
* @return void
*/
public function setExcludeFiles(array $excludeFiles)
{
foreach ($excludeFiles as $pos => $entry) {
if (strpos($entry, '*') !== false) {
$entry = '/^' . str_replace('*', '.*', preg_quote($entry)) . '$/';
$excludeFiles[$pos] = $entry;
}
}
$this->_excludeFiles = $excludeFiles;
}
/**
* Returns list of files to exclude
*
* @return array
*/
public function getExcludeFiles()
{
return $this->_excludeFiles;
}
/**
* Sets file extensions to parse
*
* @param array $extensionsToParse
* @return void
*/
public function setExtensionsToParse(array $extensionsToParse)
{
$this->_extensionsToParse = $extensionsToParse;
}
/**
* Returns list of file extension to parse
*
* @return array
*/
public function getExtensionsToParse()
{
return $this->_extensionsToParse;
}
/**
* Detects all available class type tokens in found files inside passed directory.
*
* @param SplFileInfo $fileInfo
* @param bool $recursive Flag to parse directory recursive
* @return array|null Either a assoziative array where the key is the class
* type token and the value is the path or null.
*/
public function findInDir(SplFileInfo $fileInfo, $recursive=true)
{
if (!$fileInfo->isDir() || !$fileInfo->isReadable()) {
$this->_debug('findInDir: Invalid/Not readable directory ' . $fileInfo->getPathname());
return null;
}
$this->_debug('findInDir: Processing dir ' . $fileInfo->getPathname() . ' (realpath: ' . $fileInfo->getRealPath() . ')');
$classTypeTokens = array();
$iterator = $this->_getDirIterator($fileInfo, $recursive);
foreach ($iterator as $file) {
if ($this->_isFileToProccess($file)) {
if ($foundTokens = $this->findInFile($file)) {
$classTypeTokens = array_merge($classTypeTokens, $foundTokens);
}
}
}
return (count($classTypeTokens) > 0) ? $classTypeTokens : null;
}
/**
* Detects all available class type tokens in passed file
*
* @param SplFileInfo $fileInfo
* @return array|null Either a assoziative array where the key is the class
* type token and the value is the path or null.
*/
public function findInFile(SplFileInfo $fileInfo)
{
if (!$fileInfo->isFile() || !$fileInfo->isReadable()) {
$this->_debug('findInFile: Invalid/Not readable file ' . $fileInfo->getPathname());
return null;
}
$this->_debug('findInFile: Processing file ' . $fileInfo->getPathname() . ' (realpath: ' . $fileInfo->getRealPath() . ')');
$classTypeTokens = array();
$tokens = token_get_all(file_get_contents($fileInfo->getRealPath()));
$prevTokenFound = false;
foreach ($tokens as $p => $token) {
if ($token[0] == T_INTERFACE) {
$this->_debug('findInFile: T_INTERFACE token found (token pos ' . $p . ')');
$prevTokenFound = true;
# } elseif ($token[0] == T_ABSTRACT) {
# $this->_debug('findInFile: T_ABSTRACT token found (token pos ' . $p . ')');
# $prevTokenFound = true;
} elseif ($token[0] == T_CLASS) {
$this->_debug('findInFile: T_CLASS token found (token pos ' . $p . ')');
$prevTokenFound = true;
}
if ($prevTokenFound && $token[0] !== T_STRING) {
continue;
} elseif ($prevTokenFound && $token[0] == T_STRING) {
$classTypeTokens[$token[1]] = $this->_normalizePathSeparator($fileInfo->getRealPath());
$prevTokenFound = false;
}
}
return (count($classTypeTokens) > 0) ? $classTypeTokens : null;
}
/**
* Returns list of debug messages
*
* @return array
*/
public function getDebugMessages()
{
return $this->_debugMessages;
}
/**
* Returns debug messages in a formatted way.
*
* @param string $delemiter Delemiter between each message
* @param string $wrap String with %s type specifier used to wrap all
* messages
* @return string Formatted string
*/
public function getFormattedDebugMessages($delemiter="\n", $wrap='%s')
{
if (strpos($wrap, '%s') === false) {
throw new Exception('Missing type specifier %s in parameter wrap!');
}
$messages = implode($delemiter, $this->_debugMessages);
return sprintf($wrap, $messages);
}
/**
* Adds passed message to debug list, if debugging is enabled
*
* @param string $msg
* @return void
*/
protected function _debug($msg)
{
if ($this->_enableDebug) {
$this->_debugMessages[] = $msg;
}
}
/**
* Returns directory iterator depending on $recursive parameter value
*
* @param SplFileInfo $file
* @param bool $recursive
* @return RecursiveIteratorIterator|DirectoryIterator
*/
protected function _getDirIterator(SplFileInfo $fileInfo, $recursive)
{
if ($recursive === true) {
return new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($fileInfo->getRealPath()),
RecursiveIteratorIterator::SELF_FIRST
);
} else {
return new DirectoryIterator($fileInfo->getRealPath());
}
}
/**
* Checks if file is to proccess
*
* @param SplFileInfo $file
* @return bool
*/
protected function _isFileToProccess(SplFileInfo $file)
{
if ($this->_isDirToExclude($file)) {
$this->_debug('_isFileToProccess: Dir to exclude ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
return false;
}
if ($this->_isFileToExclude($file)) {
$this->_debug('_isFileToProccess: File to exclude ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
return false;
}
if ($this->_isFileToParse($file)) {
$this->_debug('_isFileToProccess: File to parse ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
return true;
}
return false;
}
/**
* Checks if directory is to exclude
*
* @param SplFileInfo $file
* @return bool
*/
protected function _isDirToExclude(SplFileInfo $file)
{
$path = strtolower($this->_normalizePathSeparator($file->getRealPath()));
foreach ($this->_excludeDirs as $item) {
if (strpos($path, $item) !== false) {
return true;
}
}
return false;
}
/**
* Checks if file is to exclude
*
* @param SplFileInfo $file
* @return bool
*/
protected function _isFileToExclude(SplFileInfo $file)
{
$path = strtolower($this->_normalizePathSeparator($file->getRealPath()));
foreach ($this->_excludeFiles as $item) {
if (strlen($item) > 2 && substr($item, 0, 2) == '/^') {
if (preg_match($item, $path)) {
return true;
}
} else if (strpos($path, $item) !== false) {
return true;
}
}
return false;
}
/**
* Checks if file is to parse (if file extension matches)
*
* @param SplFileInfo $file
* @return bool
*/
protected function _isFileToParse(SplFileInfo $file)
{
$path = strtolower($this->_normalizePathSeparator($file->getRealPath()));
foreach ($this->_extensionsToParse as $item) {
if (substr($path, -strlen($item)) == $item) {
return true;
}
}
return false;
}
/**
* Replaces windows style directory separator (backslash against slash)
*
* @param string
* @return string
*/
protected function _normalizePathSeparator($path)
{
if (DIRECTORY_SEPARATOR == '\\') {
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
}
return $path;
}
}