Skip to content

Instantly share code, notes, and snippets.

@shevron
Created January 10, 2012 18:31
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save shevron/1590399 to your computer and use it in GitHub Desktop.
Save shevron/1590399 to your computer and use it in GitHub Desktop.
Zend Framework 2.0 style autoloader classmap generator task for Phing
<?php
/**
* Phing task to generate a Zend Framework style autoloader classmap file
*
* This task requires ZF 2.x to be in your include_path. You can run phing like
* so to enforce this:
*
* $ export PHP_COMMAND="php -d include_path=.:<path to zf library>:<path to pear>"
* $ phing ...
*
* Usage example in a phing build.xml file:
*
* <target name="generate-classmap">
* <taskdef classname="phing.tasks.ZfClassmapTask" name="classmap" />
*
* <!-- generate a classmap for library classes -->
* <classmap outputFile="${builddir}/library/autoload_classmap.php">
* <dirset dir="${builddir}/library">
* <include name="Zend" />
* <include name="MyLibrary" />
* </dirset>
* </classmap>
*
* <!-- generate a classmap for the MyApplication module -->
* <classmap outputFile="${builddir}/module/MyApplication/autoload_classmap.php">
* <dirset dir="${builddir}/module" includes="MyApplication" />
* </classmap>
* </target>
*
* This will generate a classmap file named 'library/autoload_classmap.php' in
* the ${builddir} directory (defined by a property), taking the "Zend" and
* "MyLibrary" directories under ${builddir}/library into account when searching
* for classes.
*
*/
require_once 'phing/Task.php';
class ZfClassmapTask extends Task
{
/**
* the source files
*
* @var DirSet
*/
protected $dirsets = array();
/**
* Define output file name
*
* @var string
*/
protected $outputFile = "autoload_classmap.php";
/**
* Define output directory
*
* @var string
*/
protected $outputDir = null;
/**
* Whether the build should fail, if errors occured
*
* @var boolean
*/
protected $failonerror = false;
/**
* Nested creator, adds a set of directories
*
*/
public function createDirSet()
{
$num = array_push($this->dirsets, new DirSet());
return $this->dirsets[$num - 1];
}
/**
* Whether the build should fail, if an error occured.
*
* @param boolean $value
*/
public function setFailonerror($value)
{
$this->failonerror = $value;
}
/**
* Set the output file
*
* @param string $outputFile
*/
public function setOutputFile($outputFile)
{
$outputDir = realpath(dirname($outputFile));
if (! $outputDir) {
throw new BuildException("Output directory '$outputDir' does not exist");
}
$this->outputDir = $outputDir;
$this->outputFile = basename($outputFile);
}
/**
* The init method: Do init steps.
*/
public function init()
{
if (false === @include_once('Zend/Loader/StandardAutoloader.php')) {
throw new BuildException("Unable to locate Zend Framework 2.x in include path");
}
$loader = new Zend\Loader\StandardAutoloader();
$loader->register();
}
/**
* The main entry point method.
*/
public function main()
{
if (null === $this->outputDir) {
$this->outputDir = getcwd();
}
foreach ($this->dirsets as $ds) { /* @var $ds DirSet */
try {
$dirs = $ds->getDirectoryScanner($this->project)->getIncludedDirectories();
$fullPath = realpath($ds->getDir($this->project));
$map = new stdClass();
foreach ($dirs as $dir) {
$dir = $fullPath . "/" . $dir;
$this->log("Processing library dir: $dir");
$locator = new Zend\File\ClassFileLocator($dir);
foreach($locator as $file) {
$this->addFileToMap($file, $map);
}
}
$this->writeMapFile($map);
} catch (BuildException $be) {
// directory doesn't exist or is not readable
if ($this->failonerror) {
throw $be;
} else {
$this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
}
}
}
}
protected function addFileToMap(SplFileInfo $file, stdClass $map)
{
$filename = $this->getRelativePathFromMapFile($file);
$namespace = empty($file->namespace) ? '' : $file->namespace . '\\';
$map->{$namespace . $file->classname} = "/" . ltrim($filename, "/");
}
protected function writeMapFile(stdClass $map)
{
$maxKeyLength = $this->findLongestKeyLength($map) + 2;
$output = "<?php\n\n" .
"/**\n" .
" * Zend Framework autoloader classmap\n" .
" *\n" .
" * Auto-generated by Phing\\" . __CLASS__ . " at " . date(DATE_RFC2822) . "\n" .
" */\n\n" .
"return ";
$classmap = var_export((array) $map, true);
// Remove unnecessary double-backslashes
$classmap = str_replace('\\\\', '\\', $classmap);
// Exchange "array (" width "array("
$classmap = str_replace('array (', 'array(', $classmap);
$replaceFunc = function ($match) use ($maxKeyLength) {
return sprintf(" %-{$maxKeyLength}s => __DIR__ .", $match[1]);
};
$output .= preg_replace_callback('/^\s+([^=]+)\s+=>/m', $replaceFunc, $classmap) . ';';
$outputFile = $this->outputDir . '/' . $this->outputFile;
if (! @file_put_contents($outputFile, $output)) {
throw new BuildException("Unable to write classmap to $outputFile");
}
$this->log("Wrote autoloader classmap file to $outputFile");
}
/**
* Get relative path to a class file from the classmap file path
*
* @param SplFileInfo $file
* @return string
*/
protected function getRelativePathFromMapFile(SplFileInfo $file)
{
if (strpos($file->getPath(), $this->outputDir) === 0) {
$relativePath = substr($file->getPath(), strlen($this->outputDir) + 1) . '/';
} else {
$relative = array();
$filePathParts = explode('/', $file->getPath());
$outputDirParts = explode('/', $this->outputDir);
foreach ($outputDirParts as $index => $part) {
if (isset($filePathParts[$index]) && $filePathParts[$index] == $part) {
continue;
}
$relative[] = '..';
}
foreach ($filePathParts as $index => $part ) {
if (isset($outputDirParts[$index]) && $outputDirParts[$index] == $part) {
continue;
}
$relative[] = $part;
}
$relativePath = implode('/', $relative) . '/';
}
return $relativePath . $file->getFilename();
}
/**
* Get the length of the longest key in an object
*
* @param stdClass $map
* @return integer
*/
protected function findLongestKeyLength(stdClass $map)
{
$max = 0;
foreach(array_keys(get_object_vars($map)) as $key) {
$max = max($max, strlen($key));
}
return $max;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment