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: public function setExcludeFiles(array $excludeFiles)
117: {
118: foreach ($excludeFiles as $pos => $entry) {
119: if (cString::findFirstPos($entry, '*') !== false) {
120: $entry = '/^' . str_replace('*', '.*', preg_quote($entry)) . '$/';
121: $excludeFiles[$pos] = $entry;
122: }
123: }
124: $this->_excludeFiles = $excludeFiles;
125: }
126:
127:
128: 129: 130: 131: 132:
133: public function getExcludeFiles()
134: {
135: return $this->_excludeFiles;
136: }
137:
138:
139: 140: 141: 142: 143:
144: public function setExtensionsToParse(array $extensionsToParse)
145: {
146: $this->_extensionsToParse = $extensionsToParse;
147: }
148:
149:
150: 151: 152: 153: 154:
155: public function getExtensionsToParse()
156: {
157: return $this->_extensionsToParse;
158: }
159:
160:
161: 162: 163: 164: 165: 166: 167: 168:
169: public function findInDir(SplFileInfo $fileInfo, $recursive=true)
170: {
171: if (!$fileInfo->isDir() || !$fileInfo->isReadable()) {
172: $this->_debug('findInDir: Invalid/Not readable directory ' . $fileInfo->getPathname());
173: return NULL;
174: }
175: $this->_debug('findInDir: Processing dir ' . $fileInfo->getPathname() . ' (realpath: ' . $fileInfo->getRealPath() . ')');
176:
177: $classTypeTokens = array();
178:
179: $iterator = $this->_getDirIterator($fileInfo, $recursive);
180:
181: foreach ($iterator as $file) {
182:
183: if ($this->_isFileToProccess($file)) {
184: if ($foundTokens = $this->findInFile($file)) {
185: $classTypeTokens = array_merge($classTypeTokens, $foundTokens);
186: }
187: }
188: }
189:
190: return (count($classTypeTokens) > 0) ? $classTypeTokens : NULL;
191: }
192:
193:
194: 195: 196: 197: 198: 199: 200:
201: public function findInFile(SplFileInfo $fileInfo)
202: {
203: if (!$fileInfo->isFile() || !$fileInfo->isReadable()) {
204: $this->_debug('findInFile: Invalid/Not readable file ' . $fileInfo->getPathname());
205: return NULL;
206: }
207: $this->_debug('findInFile: Processing file ' . $fileInfo->getPathname() . ' (realpath: ' . $fileInfo->getRealPath() . ')');
208:
209: $classTypeTokens = array();
210:
211: $tokens = token_get_all(file_get_contents($fileInfo->getRealPath()));
212: $prevTokenFound = false;
213: foreach ($tokens as $p => $token) {
214: if ($token[0] == T_INTERFACE) {
215: $this->_debug('findInFile: T_INTERFACE token found (token pos ' . $p . ')');
216: $prevTokenFound = true;
217:
218:
219:
220: } elseif ($token[0] == T_CLASS) {
221: $this->_debug('findInFile: T_CLASS token found (token pos ' . $p . ')');
222: $prevTokenFound = true;
223: }
224: if ($prevTokenFound && $token[0] !== T_STRING) {
225: continue;
226: } elseif ($prevTokenFound && $token[0] == T_STRING) {
227: $classTypeTokens[$token[1]] = $this->_normalizePathSeparator($fileInfo->getRealPath());
228: $prevTokenFound = false;
229: }
230: }
231:
232: return (count($classTypeTokens) > 0) ? $classTypeTokens : NULL;
233: }
234:
235:
236: 237: 238: 239: 240:
241: public function getDebugMessages()
242: {
243: return $this->_debugMessages;
244: }
245:
246:
247: 248: 249: 250: 251: 252: 253: 254: 255:
256: public function getFormattedDebugMessages($delemiter="\n", $wrap='%s')
257: {
258: if (cString::findFirstPos($wrap, '%s') === false) {
259: throw new cInvalidArgumentException('Missing type specifier %s in parameter wrap!');
260: }
261: $messages = implode($delemiter, $this->_debugMessages);
262: return sprintf($wrap, $messages);
263: }
264:
265:
266: 267: 268: 269: 270:
271: protected function _debug($msg)
272: {
273: if ($this->_enableDebug) {
274: $this->_debugMessages[] = $msg;
275: }
276: }
277:
278:
279: 280: 281: 282: 283: 284: 285:
286: protected function _getDirIterator(SplFileInfo $fileInfo, $recursive)
287: {
288: if ($recursive === true) {
289: return new RecursiveIteratorIterator(
290: new RecursiveDirectoryIterator($fileInfo->getRealPath()),
291: RecursiveIteratorIterator::SELF_FIRST
292: );
293: } else {
294: return new DirectoryIterator($fileInfo->getRealPath());
295: }
296: }
297:
298:
299: 300: 301: 302: 303: 304:
305: protected function _isFileToProccess(SplFileInfo $file)
306: {
307: if ($this->_isDirToExclude($file)) {
308: $this->_debug('_isFileToProccess: Dir to exclude ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
309: return false;
310: }
311: if ($this->_isFileToExclude($file)) {
312: $this->_debug('_isFileToProccess: File to exclude ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
313: return false;
314: }
315: if ($this->_isFileToParse($file)) {
316: $this->_debug('_isFileToProccess: File to parse ' . $file->getPathname() . ' (realpath: ' . $file->getRealPath() . ')');
317: return true;
318: }
319: return false;
320: }
321:
322:
323: 324: 325: 326: 327: 328:
329: protected function _isDirToExclude(SplFileInfo $file)
330: {
331: $path = cString::toLowerCase($this->_normalizePathSeparator($file->getRealPath()));
332:
333: foreach ($this->_excludeDirs as $item) {
334: if (cString::findFirstPos($path, $item) !== false) {
335: return true;
336: }
337: }
338: return false;
339: }
340:
341:
342: 343: 344: 345: 346: 347:
348: protected function _isFileToExclude(SplFileInfo $file)
349: {
350: $path = cString::toLowerCase($this->_normalizePathSeparator($file->getRealPath()));
351:
352: foreach ($this->_excludeFiles as $item) {
353: if (cString::getStringLength($item) > 2 && cString::getPartOfString($item, 0, 2) == '/^') {
354: if (preg_match($item, $path)) {
355: return true;
356: }
357: } else if (cString::findFirstPos($path, $item) !== false) {
358: return true;
359: }
360: }
361: return false;
362: }
363:
364:
365: 366: 367: 368: 369: 370:
371: protected function _isFileToParse(SplFileInfo $file)
372: {
373: $path = cString::toLowerCase($this->_normalizePathSeparator($file->getRealPath()));
374:
375: foreach ($this->_extensionsToParse as $item) {
376: if (cString::getPartOfString($path, -cString::getStringLength($item)) == $item) {
377: return true;
378: }
379: }
380: return false;
381: }
382:
383:
384: 385: 386: 387: 388: 389:
390: protected function _normalizePathSeparator($path)
391: {
392: if (DIRECTORY_SEPARATOR == '\\') {
393: $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
394: }
395: return $path;
396: }
397:
398: }
399: