1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14: 15: 16: 17: 18: 19: 20:
21: class mpClassTypeFinder
22: {
23: 24: 25: 26:
27: protected $_excludeDirs = array('.svn', '.cvs');
28:
29: 30: 31: 32:
33: protected $_excludeFiles = array('/^~*.\.php$/', '/^~*.\.inc$/');
34:
35: 36: 37: 38:
39: protected $_extensionsToParse = array('.php', '.inc');
40:
41: 42: 43: 44: 45:
46: protected $_enableDebug = false;
47:
48: 49: 50: 51:
52: protected $_debugMessages = array();
53:
54:
55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67:
68: public function __construct(array $options=array())
69: {
70: if (isset($options['excludeDirs']) && is_array($options['excludeDirs'])) {
71: $this->setExcludeDirs($options['excludeDirs']);
72: }
73: if (isset($options['excludeFiles']) && is_array($options['excludeFiles'])) {
74: $this->setExcludeFiles($options['excludeFiles']);
75: }
76: if (isset($options['extensionsToParse']) && is_array($options['extensionsToParse'])) {
77: $this->setExtensionsToParse($options['extensionsToParse']);
78: }
79: if (isset($options['enableDebug']) && is_bool($options['enableDebug'])) {
80: $this->_enableDebug = $options['enableDebug'];
81: }
82: }
83:
84:
85: 86: 87: 88: 89: 90:
91: public function setExcludeDirs(array $excludeDirs)
92: {
93: $this->_excludeDirs = $excludeDirs;
94: }
95:
96:
97: 98: 99: 100: 101:
102: public function getExcludeDirs()
103: {
104: return $this->_excludeDirs;
105: }
106:
107:
108: 109: 110: 111: 112: 113: 114: 115: 116:
117: public function setExcludeFiles(array $excludeFiles)
118: {
119: foreach ($excludeFiles as $pos => $entry) {
120: if (strpos($entry, '*') !== false) {
121: $entry = '/^' . str_replace('*', '.*', preg_quote($entry)) . '$/';
122: $excludeFiles[$pos] = $entry;
123: }
124: }
125: $this->_excludeFiles = $excludeFiles;
126: }
127:
128:
129: 130: 131: 132: 133:
134: public function getExcludeFiles()
135: {
136: return $this->_excludeFiles;
137: }
138:
139:
140: 141: 142: 143: 144: 145:
146: public function setExtensionsToParse(array $extensionsToParse)
147: {
148: $this->_extensionsToParse = $extensionsToParse;
149: }
150:
151:
152: 153: 154: 155: 156:
157: public function getExtensionsToParse()
158: {
159: return $this->_extensionsToParse;
160: }
161:
162:
163: 164: 165: 166: 167: 168: 169: 170:
171: public function findInDir(SplFileInfo $fileInfo, $recursive=true)
172: {
173: if (!$fileInfo->isDir() || !$fileInfo->isReadable()) {
174: $this->_debug('findInDir: Invalid/Not readable directory ' . $fileInfo->getPathname());
175: return null;
176: }
177: $this->_debug('findInDir: Processing dir ' . $fileInfo->getPathname() . ' (realpath: ' . $fileInfo->getRealPath() . ')');
178:
179: $classTypeTokens = array();
180:
181: $iterator = $this->_getDirIterator($fileInfo, $recursive);
182:
183: foreach ($iterator as $file) {
184:
185: if ($this->_isFileToProccess($file)) {
186: if ($foundTokens = $this->findInFile($file)) {
187: $classTypeTokens = array_merge($classTypeTokens, $foundTokens);
188: }
189: }
190: }
191:
192: return (count($classTypeTokens) > 0) ? $classTypeTokens : null;
193: }
194:
195:
196: 197: 198: 199: 200: 201: 202:
203: public function findInFile(SplFileInfo $fileInfo)
204: {
205: if (!$fileInfo->isFile() || !$fileInfo->isReadable()) {
206: $this->_debug('findInFile: Invalid/Not readable file ' . $fileInfo->getPathname());
207: return null;
208: }
209: $this->_debug('findInFile: Processing file ' . $fileInfo->getPathname() . ' (realpath: ' . $fileInfo->getRealPath() . ')');
210:
211: $classTypeTokens = array();
212:
213: $tokens = token_get_all(file_get_contents($fileInfo->getRealPath()));
214: $prevTokenFound = false;
215: foreach ($tokens as $p => $token) {
216: if ($token[0] == T_INTERFACE) {
217: $this->_debug('findInFile: T_INTERFACE token found (token pos ' . $p . ')');
218: $prevTokenFound = true;
219:
220:
221:
222: } elseif ($token[0] == T_CLASS) {
223: $this->_debug('findInFile: T_CLASS token found (token pos ' . $p . ')');
224: $prevTokenFound = true;
225: }
226: if ($prevTokenFound && $token[0] !== T_STRING) {
227: continue;
228: } elseif ($prevTokenFound && $token[0] == T_STRING) {
229: $classTypeTokens[$token[1]] = $this->_normalizePathSeparator($fileInfo->getRealPath());
230: $prevTokenFound = false;
231: }
232: }
233:
234: return (count($classTypeTokens) > 0) ? $classTypeTokens : null;
235: }
236:
237:
238: 239: 240: 241: 242:
243: public function getDebugMessages()
244: {
245: return $this->_debugMessages;
246: }
247:
248:
249: 250: 251: 252: 253: 254: 255: 256: 257:
258: public function getFormattedDebugMessages($delemiter="\n", $wrap='%s')
259: {
260: if (strpos($wrap, '%s') === false) {
261: throw new cInvalidArgumentException('Missing type specifier %s in parameter wrap!');
262: }
263: $messages = implode($delemiter, $this->_debugMessages);
264: return sprintf($wrap, $messages);
265: }
266:
267:
268: 269: 270: 271: 272: 273:
274: protected function _debug($msg)
275: {
276: if ($this->_enableDebug) {
277: $this->_debugMessages[] = $msg;
278: }
279: }
280:
281:
282: 283: 284: 285: 286: 287: 288:
289: protected function _getDirIterator(SplFileInfo $fileInfo, $recursive)
290: {
291: if ($recursive === true) {
292: return new RecursiveIteratorIterator(
293: new RecursiveDirectoryIterator($fileInfo->getRealPath()),
294: RecursiveIteratorIterator::SELF_FIRST
295: );
296: } else {
297: return new DirectoryIterator($fileInfo->getRealPath());
298: }
299: }
300:
301:
302: 303: 304: 305: 306: 307:
308: protected function _isFileToProccess(SplFileInfo $file)
309: {
310: if ($this->_isDirToExclude($file)) {
311: $this->_debug('_isFileToProccess: Dir to exclude ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
312: return false;
313: }
314: if ($this->_isFileToExclude($file)) {
315: $this->_debug('_isFileToProccess: File to exclude ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
316: return false;
317: }
318: if ($this->_isFileToParse($file)) {
319: $this->_debug('_isFileToProccess: File to parse ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
320: return true;
321: }
322: return false;
323: }
324:
325:
326: 327: 328: 329: 330: 331:
332: protected function _isDirToExclude(SplFileInfo $file)
333: {
334: $path = strtolower($this->_normalizePathSeparator($file->getRealPath()));
335:
336: foreach ($this->_excludeDirs as $item) {
337: if (strpos($path, $item) !== false) {
338: return true;
339: }
340: }
341: return false;
342: }
343:
344:
345: 346: 347: 348: 349: 350:
351: protected function _isFileToExclude(SplFileInfo $file)
352: {
353: $path = strtolower($this->_normalizePathSeparator($file->getRealPath()));
354:
355: foreach ($this->_excludeFiles as $item) {
356: if (strlen($item) > 2 && substr($item, 0, 2) == '/^') {
357: if (preg_match($item, $path)) {
358: return true;
359: }
360: } else if (strpos($path, $item) !== false) {
361: return true;
362: }
363: }
364: return false;
365: }
366:
367:
368: 369: 370: 371: 372: 373:
374: protected function _isFileToParse(SplFileInfo $file)
375: {
376: $path = strtolower($this->_normalizePathSeparator($file->getRealPath()));
377:
378: foreach ($this->_extensionsToParse as $item) {
379: if (substr($path, -strlen($item)) == $item) {
380: return true;
381: }
382: }
383: return false;
384: }
385:
386:
387: 388: 389: 390: 391: 392:
393: protected function _normalizePathSeparator($path)
394: {
395: if (DIRECTORY_SEPARATOR == '\\') {
396: $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
397: }
398: return $path;
399: }
400:
401: }
402: