/** * Extends PHP glob with recursion and base path (files only) * * @param string|array $paths Paths to find files (base paths). * Default = system current directory. * @param string|array $filePatterns RegExp patterns to filter files (to find files). * Default = "" - all files. * @param string|array $excludePatterns RegExp patterns to filter files (to ignore files). * Default = NULL - no files to exclusion. * @param boolean $recursive =TRUE - find files in sub directories. * Default = TRUE. * @return array A hash array of found files. Hash structure: * @code * array( * "/absolute/file/name1" => "/base/path" * "/absolute/file/name2" => "/base/path" * ... * ) * @endcode * * Exapmle: * @code print_r( Files::globFiles('/base/path/', '\.php$') ); //-------- // Return: Array ( '/base/path/file1.php' => '/base/path', '/base/path/file2.php' => '/base/path', '/base/path/folder1/file1_in_folder1.php' => '/base/path', '/base/path/folder1/file2_in_folder1.php' => '/base/path', '/base/path/folder1/subfolder1/file1_in_subfolder1.php' => '/base/path', ) * @endcode */ public static function globFiles($paths = array(''), $filePatterns = array(''), $excludePatterns = NULL, $recursive = true) { //--- Convert $paths to array: if (!is_array($paths)) { $paths = array($paths); } //--- Convert $filePatterns to array: if (!is_array($filePatterns)) { $filePatterns = array($filePatterns); } //--- Convert $excludePatterns to array: if ($excludePatterns && !is_array($excludePatterns)) { $excludePatterns = array($excludePatterns); } //--- Get all source paths: $sourcePaths = array(); foreach ($paths as $sourcePath) { $sourcePath = realpath($sourcePath); $sourcePaths[$sourcePath] = $sourcePath; if ($sourcePath && is_array($out = self::glob($sourcePath, "*", GLOB_ONLYDIR, $recursive))) { foreach ($out as $path) { $sourcePaths[$path] = $sourcePath; } } } //--- Get all source files and filter it: foreach ($sourcePaths as $path => $base) { if (is_array($files = self::glob($path))) { foreach ($files as $file) { if (!is_file($file)) { continue; } $name = basename($file); if (Basic::inPatterns($name, $filePatterns) && !Basic::inPatterns($name, $excludePatterns)) { $outSources[$file] = $base; } } } } return $outSources; }
/** * To find all methods of class into a source code of a given class * * @param string $data Text data contains a class source code. * @return <i>array</i> Array structure: * ~~~ * array( * "method1" => array( * "type" => static | abstract | "", * "scope" => private | protected | private , * "params" => "parameters of methods" * ) * "method2" => array( ... ), * ... * ) * ~~~ */ public static function getClassMethods($data) { if (preg_match_all('/^\\s*([\\w\\s]*)\\s*function\\s+([\\w_]+)\\s*\\(([^\\{]*)\\)[^\\{\\)]*\\{/ms', $data, $m)) { foreach ($m[1] as $i => $value) { $methodProp = $m[1][$i]; $methodScop = Basic::inPatterns($methodProp, array("public", "protected", "private"), true); if (!$methodScop) { $methodScop = "public"; } $methodType = Basic::inPatterns($methodProp, array("abstract"), true); if (!$methodType) { $methodType = Basic::inPatterns($methodProp, array("static"), true); } $methodName = $m[2][$i]; $methodPara = $m[3][$i]; $methods[$methodName] = array("scope" => $methodScop, "type" => $methodType, "params" => $methodPara); } } return $methods; }
/** * Generate code with PHPUnit tests for classes and functions of source code * * @param string $sourceCode PHP-file source code. * @param string $sourceFileName (Option) needed to create "require" directive. * @param string $resultFileName (Option) needed to create class name for stanalong functions and to create "require" directive with relative path. * @param string $currentContent (Option) needed to insert result code inside an existing content. * @return string Result code of PHP-file contains tests. */ public function generateCode($sourceCode, $sourceFileName = "", $resultFileName = "NonameClass", $currentContent = NULL) { //--- Prepare excludeNames Array: if ($this->excludeNames && !is_array($this->excludeNames)) { $this->excludeNames = array($this->excludeNames); } //--- Create class name for functions: $baseClassName = preg_replace("/\\..*\$/", "", basename($resultFileName)); //--- Create source code structure: $codeStructure = SimpleCodeParser::parseCode($sourceCode); //--- Create out structure for tests: foreach ($codeStructure as $nameSpace => $body) { //--- Create Namespaces: if ($nameSpace) { $useNameSpaces[] = $nameSpace; $nameSpacePrefix = basename(str_replace("\\", "/", $nameSpace)) . "\\"; $nameSpaceFull = $nameSpace . "\\"; } else { $nameSpacePrefix = ""; $nameSpaceFull = ""; } //--- Create Tests for Classes: if (is_array($body["classes"])) { foreach ($body["classes"] as $className => $methods) { $sourceClassList[] = " class: " . $nameSpaceFull . $className; $testClassName = Basic::replace($this->testClassName, array("NAME" => $className)); $testClassMap[$testClassName] = $nameSpaceFull . $className; if (is_array($methods)) { foreach ($methods as $methodName => $params) { if (($params["scope"] == "" || $params["scope"] == "public") && !Basic::inPatterns($methodName, $this->excludeNames)) { $add = false; if ($params["type"] == "static") { $name = "static method: " . $nameSpaceFull . "{$className}::{$methodName}"; $create = ""; $instance = $nameSpacePrefix . "{$className}::{$methodName}(" . $this->stripFunctionParams($params["params"]) . ")"; $add = true; } elseif ($params["type"] == "") { $name = "method: " . $nameSpaceFull . "{$className}->{$methodName}"; $create = "\$c = new " . $nameSpacePrefix . "{$className}();"; $instance = "\$c->{$methodName}(" . $this->stripFunctionParams($params["params"]) . ")"; $add = true; } if (!$add) { continue; } //--- Add Tests to outStructure: $testName = Basic::replace($this->testName, array("NAME" => $methodName)); $outStructure[$testClassName][$testName] = Basic::replace($this->templateTest, array("NAME" => $name, "TEST_NAME" => $testName, "CREATE" => $create, "INSTANCE" => $instance)); } } } } } //--- Create Tests for functions: if (is_array($body["functions"])) { foreach ($body["functions"] as $functionName => $params) { if (Basic::inPatterns($functionName, $this->excludeNames)) { continue; } $name = "function: " . $nameSpaceFull . $functionName; $sourceClassList[] = $name; //--- Add Tests to outStructure: $testClassName = $baseClassName; $testName = Basic::replace($this->testName, array("NAME" => $functionName)); $outStructure[$testClassName][$testName] = Basic::replace($this->templateTest, array("NAME" => $name, "TEST_NAME" => $testName, "CREATE" => "", "INSTANCE" => $nameSpacePrefix . "{$functionName}(" . $this->stripFunctionParams($params) . ")")); } } } //--- If outStructure is empty - break: if (!$outStructure) { return false; } //--- Get existing result code structure (if it was saved earlier): $existingStructure = SimpleCodeParser::parseCode($currentContent); //--- Generate new result code: $resultCode = $currentContent; //--- Add Header: if ($resultCode == "") { $resultCode = Basic::replace($this->templateHeader, array("DATE" => date("Y.m.d H:i"), "GENERATOR_NAME" => get_class())); } //--- Add some comments: if (is_array($sourceClassList)) { sort($sourceClassList); $sourceClassList = implode("\n * ", $sourceClassList); } $comments = Basic::replace($this->templateComments, array("SOURCE_FILE" => basename($sourceFileName), "CLASS_LIST" => $sourceClassList)); $resultCode = preg_replace('/( \\*=+\\n)([^=]*?)( \\*\\/)/ms', '\\1' . $comments . '\\3', $resultCode); //--- Add testNameSpace: if ($this->testNameSpace) { $testNameSpace = "\n\n" . "namespace " . $this->testNameSpace . ";\n"; $regExpPattern = '^\\s*namespace\\s+' . preg_quote($this->testNameSpace, "/") . '\\s*;'; $resultCode = self::placeAfter($resultCode, $testNameSpace, $regExpPattern); } //--- Add "USE" directive: if (is_array($useNameSpaces)) { foreach ($useNameSpaces as $nameSpace) { $useNameSpaceString = "\n\n" . "use {$nameSpace};"; $regExpPattern = '^\\s*use\\s+' . preg_quote($nameSpace, "/") . '\\s*;'; $resultCode = self::placeAfter($resultCode, $useNameSpaceString, $regExpPattern, array('^\\s*(require|require_once)\\s+[^;]+;', '^\\s*namespace\\s+[^;]+;', '^\\s*<\\?php')); } } //--- Add "REQUIRE" directive: if ($sourceFileName) { $includeSourceFileName = $sourceFileName; if ($this->includeRelativePath) { $relativeSourcePath = Files::getRelativePath(dirname($includeSourceFileName), dirname($resultFileName)); $includeSourceFileName = $relativeSourcePath["relative"] . "/" . basename($includeSourceFileName); $require = "\n\n" . "require_once __DIR__ . '{$includeSourceFileName}';"; } else { $require = "\n\n" . "require_once '{$includeSourceFileName}';"; } $regExpPattern = "^\\s*(require|require_once)\\s+[^;]*\\(?\\s*['\"]+" . preg_quote($includeSourceFileName, "/") . "['\"]+\\s*\\)?\\s*;"; $resultCode = self::placeAfter($resultCode, $require, $regExpPattern, array('^\\s*namespace\\s+[^;]+;', '^\\s*<\\?php')); } //--- Add "REQUIRE AUTOLOADER" directive: if ($this->includeAutoloaderFileName) { $includeAutoloaderFileName = realpath($this->includeAutoloaderFileName); if (!$includeAutoloaderFileName) { throw new Exception('Autoloader not found : [' . $this->includeAutoloaderFileName . ']'); } $relativeSourcePath = Files::getRelativePath(dirname($includeAutoloaderFileName), dirname($resultFileName)); $includeAutoloaderFileName = $relativeSourcePath["relative"] . "/" . basename($includeAutoloaderFileName); $require = "\n\n" . "require_once __DIR__ . '{$includeAutoloaderFileName}';"; $regExpPattern = "^\\s*(require|require_once)\\s+[^;]*\\(?\\s*['\"]+" . preg_quote($includeAutoloaderFileName, "/") . "['\"]+\\s*\\)?\\s*;"; $resultCode = self::placeAfter($resultCode, $require, $regExpPattern, array('^\\s*namespace\\s+[^;]+;', '^\\s*<\\?php')); } //--- Add file body: if (is_array($outStructure)) { //--- Add classes foreach ($outStructure as $className => $methods) { if (is_array($existingStructure[$this->testNameSpace]["classes"]) && array_key_exists($className, $existingStructure[$this->testNameSpace]["classes"])) { continue; } $resultCode .= Basic::replace($this->templateClass, array("CLASS_NAME" => $testClassMap[$className], "TEST_CLASS_NAME" => $className)); } //--- Add methods foreach ($outStructure as $className => $methods) { $classBody = ""; foreach ($methods as $methodName => $methodBody) { if (is_array($existingStructure[$this->testNameSpace]["classes"][$className]) && array_key_exists($methodName, $existingStructure[$this->testNameSpace]["classes"][$className])) { continue; } $classBody .= $methodBody; } if (preg_match('/^\\s*class\\s+' . preg_quote($className, "/") . '\\s+([^\\{]*)/ms', $resultCode, $m, PREG_OFFSET_CAPTURE)) { list($startPosition, $endPosition) = SimpleCodeParser::getFirstBraceBlockPos(substr($resultCode, $m[0][1])); $endPosition += $m[0][1]; $resultCode = substr($resultCode, 0, $endPosition) . $classBody . substr($resultCode, $endPosition); } } } //--- Change Update time in header --- $resultCode = preg_replace("/Updated on : \\d\\d\\d\\d\\.\\d\\d\\.\\d\\d \\d\\d:\\d\\d/", 'Updated on : ' . date("Y.m.d H:i"), $resultCode); //------------------------------------ //print_r($resultCode); exit; return $resultCode; }