-
-
Save hassankhan/6196491 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
$help = <<<'HELP' | |
EXAMPLES | |
./project-phpunit-skelgen.php -s <dir> -o <dir> -b <file> | |
DESCRIPTION | |
Recursively find PHP files and run phpunit-skelgen to create unit test skeleton files. | |
Files must use the .php extension and be PSR-0 compliant, contain a namespace and class name. | |
Files ending in 'Interface.php' are ignored. | |
OPTIONS | |
-s <dir>, --source <dir> | |
Source directory to scan for PHP files | |
-t <dir>, --target <dir> | |
Target for the generated UnitTest files. Default is current-working-dir/tests. | |
-b <file>, --bootstrap <file> | |
Specify a bootstrap file to be used by phpunit-skelgen for loading the classes. | |
-v, --verbose | |
Switch on verbose output | |
-h, --help | |
Prints this help | |
HELP; | |
$options = getopt('s:t::hb:v', array( | |
'source:', | |
'target::', | |
'bootstrap:', | |
'help', | |
'verbose', | |
)); | |
if (empty($options) || !is_null(optval($options, 'h', 'help'))) { | |
echo "$help\n"; | |
return; | |
} | |
// Extract the inputs | |
$source = optval($options, 's', 'source'); | |
$target = optval($options, 't', 'target'); | |
$bootstrap = optval($options, 'b', 'bootstrap'); | |
$verbose = !is_null(optval($options, 'v', 'verbose')); | |
// Validate inputs | |
if (empty($source) || !is_dir($source)) { | |
echo "Source given '$source' is not a directory\n"; | |
} | |
if (empty($target)) { | |
$target = './tests'; | |
} | |
if (!is_dir($target)) { | |
mkdir($target, 0777, true); | |
} | |
$sourceDir = realpath($source); | |
$targetDir = realpath($target); | |
$binary = 'phpunit-skelgen.phar'; | |
$skelgenBinary = trim(shell_exec("which $binary")); | |
if (!file_exists($skelgenBinary)) { | |
echo "Unable to locate $binary\n"; | |
} | |
// Scan and process the source directory | |
$dirIterator = new RecursiveIteratorIterator( | |
new RecursiveDirectoryIterator($sourceDir) | |
); | |
$sourcePathStrLength = strlen($sourceDir); | |
$escapedBootstrap = $bootstrap ? '--bootstrap '.escapeshellarg("$bootstrap") : ''; | |
foreach ($dirIterator as $filePath => $fileInfo) { | |
// @var SplFileInfo $fileInfo | |
if ( | |
$fileInfo->isDir() || | |
substr($filePath, -13) === 'Interface.php' | |
) { | |
continue; | |
} | |
$targetFilePath = $targetDir . preg_replace( | |
'/\.php$/i', | |
'Test.php', | |
substr($filePath, $sourcePathStrLength) | |
); | |
if (file_exists($targetFilePath)) { | |
echo "Skip: Test file for '$filePath' already exists\n"; | |
continue; | |
} | |
$fullClassName = extractFullClassNameFromFile($filePath); | |
if ($fullClassName === false) { | |
echo "Err: Class name could not be extracted from '$filePath'\n"; | |
continue; | |
} | |
$targetFileName = basename($targetFilePath); | |
$sourceDirPath = dirname($filePath); | |
$targetDirPath = dirname($targetFilePath); | |
$generatedFilePath = "$sourceDirPath/$targetFileName"; | |
if (!is_dir($targetDirPath)) { | |
mkdir($targetDirPath, 0777, true); | |
} | |
$escapedClassName = escapeshellarg($fullClassName); | |
$escapedSourcePath = escapeshellarg($filePath); | |
$result = shell_exec( | |
"$skelgenBinary $escapedBootstrap --test -- $escapedClassName $escapedSourcePath" | |
); | |
if ($verbose) { | |
echo $result; | |
} | |
if (!file_exists($generatedFilePath)) { | |
echo "Error: Failed to generate test file for '$fullClassName'\n"; | |
continue; | |
} | |
rename($generatedFilePath, $targetFilePath); | |
echo "OK: Test file successfully created for '$fullClassName'\n"; | |
} | |
echo "DONE.\n"; | |
/* ---- FUNCTIONS ---- */ | |
/** | |
* Extract an option from the results of getopt specifying the | |
* keys in order of preference. | |
* | |
* @param array $options Result from getopt('ab:c::', array('add', 'bacon', 'create')) | |
* @param string $key1 First key to look for e.g. 'add' | |
* @param string $key2 Second key to look for e.g. 'a' | |
* @param string $keyn Other possible aliases... | |
* @return mixed The value from options array or null otherwise | |
*/ | |
function optval(array &$options, $key1, $key2, $keyn = null) | |
{ | |
$args = func_get_args(); | |
$options = array_shift($args); | |
foreach ($args as $key) { | |
if (isset($options[$key])) { | |
return $options[$key]; | |
} | |
} | |
return null; | |
} | |
/** | |
* Conert to boolean by using normal PHP falsey equivalencies with | |
* the addition of string 'false' which PHP usually considers truthy. | |
* | |
* @param mixed $val Value to convert to boolean | |
* @return boolean Boolean equivalent | |
*/ | |
function bool($val) | |
{ | |
if ( | |
$val === '' || | |
$val === 'false' || | |
$val === '0' || | |
$val === 0 || | |
$val === null || | |
$val === array() || | |
$val === 0.0 || | |
$val === false | |
) { | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Extract a fully qualfied (namespaced) classname from a php file. | |
* This function assumes PSR-0 compliance. | |
* | |
* @param string $filePath Path to the php file. | |
* @return string|false The resulting classname | |
*/ | |
function extractFullClassNameFromFile($filePath) | |
{ | |
if (!file_exists($filePath)) { | |
return false; | |
} | |
$namespace = null; | |
$classname = null; | |
$cnMatches = null; | |
$nsMatches = null; | |
$file = file_get_contents($filePath); | |
if (preg_match_all('/\n\s*(abstract\s|final\s)*class\s+(?<name>[^\s;]+)\s*/i', $file, $cnMatches, PREG_PATTERN_ORDER)) { | |
$classname = array_pop($cnMatches['name']); | |
if (preg_match_all('/namespace\s+(?<name>[^\s;]+)\s*;/i', $file, $nsMatches, PREG_PATTERN_ORDER)) { | |
$namespace = array_pop($nsMatches['name']); | |
} | |
} | |
if (empty($classname)) { | |
return false; | |
} | |
return "$namespace\\$classname"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment