/src/DocBlox/Transformer.php
PHP | 423 lines | 301 code | 25 blank | 97 comment | 13 complexity | 04a261b6fcc4d5273e4513ab6b8460ec MD5 | raw file
Possible License(s): BSD-3-Clause
- <?php
- /**
- * DocBlox
- *
- * PHP Version 5
- *
- * @category DocBlox
- * @package Transformer
- * @author Mike van Riel <mike.vanriel@naenius.com>
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://docblox-project.org
- */
- /**
- * Core class responsible for transforming the structure.xml file to a set of artifacts.
- *
- * @category DocBlox
- * @package Transformer
- * @author Mike van Riel <mike.vanriel@naenius.com>
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://docblox-project.org
- */
- class DocBlox_Transformer extends DocBlox_Core_Abstract
- {
- /** @var string|null Target location where to output the artifacts */
- protected $target = null;
- /** @var DOMDocument|null DOM of the structure as generated by the parser. */
- protected $source = null;
- /** @var string[] */
- protected $templates = array();
- /** @var DocBlox_Transformer_Transformation[] */
- protected $transformations = array();
- /**
- * Initialize the transformations.
- */
- public function __construct()
- {
- $this->loadTransformations();
- }
- /**
- * Sets the target location where to output the artifacts.
- *
- * @throws Exception if the target is not a valid writable directory.
- *
- * @param string $target The target location where to output the artifacts.
- *
- * @return void
- */
- public function setTarget($target)
- {
- $path = realpath($target);
- if (!file_exists($path) && !is_dir($path) && !is_writable($path)) {
- throw new Exception('Given target directory (' . $target . ') does not exist or is not writable');
- }
- $this->target = $path;
- }
- /**
- * Returns the location where to store the artifacts.
- *
- * @return string
- */
- public function getTarget()
- {
- return $this->target;
- }
- /**
- * Sets the location of the structure file.
- *
- * @throws Exception if the source is not a valid readable file.
- *
- * @param string $source The location of the structure file as full path (may be relative).
- *
- * @return void
- */
- public function setSource($source)
- {
- $source = trim($source);
- $xml = new DOMDocument();
- if (substr($source, 0, 5) === '<?xml') {
- $xml->loadXML($source);
- } else {
- $path = realpath($source);
- if (!file_exists($path) || !is_readable($path) || !is_file($path)) {
- throw new Exception('Given source (' . $source . ') does not exist or is not readable');
- }
- // convert to dom document so that the writers do not need to
- $xml->load($path);
- }
- $this->source = $xml;
- }
- /**
- * Returns the source Structure.
- *
- * @return null|DOMDocument
- */
- public function getSource()
- {
- return $this->source;
- }
- /**
- * Sets one or more templates as basis for the transformations.
- *
- * @param string|string[] $template
- *
- * @return void
- */
- public function setTemplates($template)
- {
- // reset
- $this->templates = array();
- $this->transformations = array();
- if (!is_array($template)) {
- $template = array($template);
- }
- foreach ($template as $item)
- {
- $this->addTemplate($item);
- }
- }
- /**
- * Returns the list of templates which are going to be adopted.
- *
- * @return string[]
- */
- public function getTemplates()
- {
- return $this->templates;
- }
- /**
- * Loads the transformation from the configuration and from the given templates and/or transformations.
- *
- * @param string[] $templates Array of template names.
- * @param Transformation[]|array[] $transformations Array of transformations
- * or arrays representing transformations.
- *
- * @see self::addTransformation() for more details regarding the array structure.
- *
- * @return void
- */
- public function loadTransformations(array $templates = array(), array $transformations = array())
- {
- /** @var Zend_Config_Xml[] $config_transformations */
- $config_transformations = $this->getConfig()->get('transformations', array());
- foreach ($config_transformations as $transformation)
- {
- // if a writer is defined then it is a template; otherwise it is a template
- if (isset($transformation->writer)) {
- $this->addTransformation($transformation->toArray());
- continue;
- }
- $this->addTemplate($transformation->name);
- }
- array_walk($templates, array($this, 'addTemplate'));
- array_walk($transformations, array($this, 'addTransformation'));
- }
- /**
- * Loads a template by name, if an additional array with details is provided it will try to load parameters from it.
- *
- * @param string $name
- * @param string[]|null $details
- *
- * @return void
- */
- public function addTemplate($name)
- {
- // if the template is already loaded we do not reload it.
- if (in_array($name, $this->getTemplates())) {
- return;
- }
- $config = $this->getConfig();
- $path = null;
- // if this is an absolute path; load the template into the configuration
- // Please note that this _could_ override an existing template when
- // you have a template in a subfolder with the same name as a default
- // template; we have left this in on purpose to allow people to override
- // templates should they choose to.
- $config_path = rtrim($name, DIRECTORY_SEPARATOR) . '/template.xml';
- if (file_exists($config_path) && is_readable($config_path)) {
- $path = rtrim($name, DIRECTORY_SEPARATOR);
- $template_name_part = basename($path);
- $cache_path = rtrim($config->paths->themes, '/\\')
- . DIRECTORY_SEPARATOR . 'cache'
- . DIRECTORY_SEPARATOR . $template_name_part;
- // move the files to a cache location and then change the path
- // variable to match the new location
- $this->copyRecursive($path, $cache_path);
- $path = $cache_path;
- // transform all directory separators to underscores and lowercase
- $name = strtolower(str_replace(
- DIRECTORY_SEPARATOR, '_',
- rtrim($name, DIRECTORY_SEPARATOR)
- ));
- $config->templates->$name = new Zend_Config_Xml($config_path);
- }
- if (!isset($config->templates->$name)) {
- throw new InvalidArgumentException('Template "' . $name . '" could not be found');
- }
- // track templates to be able to refer to them later
- $this->templates[] = $name;
- // template does not have transformations; return
- if (!isset($config->templates->$name->transformations)) {
- return;
- }
- $transformations = $config->templates->$name->transformations->transformation->toArray();
- // if the array key is not numeric; then there is a single value instead of an array of transformations
- $transformations = (is_numeric(key($transformations)))
- ? $transformations
- : array($transformations);
- foreach ($transformations as $transformation)
- {
- // if this is an externally loaded template we add the template_path
- // as parameter as this is used as extra option when determining
- // where the source of a transformation may lie.
- if ($path !== null)
- {
- if (isset($transformation['parameters']))
- {
- $transformation['parameters'] = array();
- }
- $transformation['parameters']['template_path'] = $path;
- }
- $this->addTransformation($transformation);
- }
- }
- /**
- * Adds the given transformation to the transformer for execution.
- *
- * It is also allowed to pass an array notation for the transformation; then this method will create
- * a transformation object out of it.
- *
- * The structure for this array must be:
- * array(
- * 'query' => <query>,
- * 'writer' => <writer>,
- * 'source' => <source>,
- * 'artifact' => <artifact>,
- * 'parameters' => array(<parameters>),
- * 'dependencies' => array(<dependencies>)
- * )
- *
- * @param Transformation|array $transformation
- *
- * @return void
- */
- public function addTransformation($transformation)
- {
- if (is_array($transformation)) {
- // check if all required items are present
- if (!key_exists('query', $transformation)
- || !key_exists('writer', $transformation)
- || !key_exists('source', $transformation)
- || !key_exists('artifact', $transformation)) {
- throw new InvalidArgumentException(
- 'Transformation array is missing elements, received: ' . var_export($transformation, true)
- );
- }
- $transformation_obj = new DocBlox_Transformer_Transformation(
- $this,
- $transformation['query'],
- $transformation['writer'],
- $transformation['source'],
- $transformation['artifact']
- );
- if (isset($transformation['parameters']) && is_array($transformation['parameters'])) {
- $transformation_obj->setParameters($transformation['parameters']);
- }
- $transformation = $transformation_obj;
- }
- // if it is still not an object; fail
- if (!is_object($transformation)) {
- throw new InvalidArgumentException(
- 'Only transformations of type (or descended from) DocBlox_Transformer_Transformation can be used in the '
- . 'transformation process; received: ' . gettype($transformation)
- );
- }
- // if the object is not a DocBlox_Transformer_Transformation; we cannot use it
- if (!$transformation instanceof DocBlox_Transformer_Transformation) {
- throw new InvalidArgumentException(
- 'Only transformations of type (or descended from) DocBlox_Transformer_Transformation can be used in the '
- . 'transformation process; received: ' . get_class($transformation)
- );
- }
- $this->transformations[] = $transformation;
- }
- /**
- * Returns the transformation which this transformer will process.
- *
- * @return DocBlox_Transformer_Transformation[]
- */
- public function getTransformations()
- {
- return $this->transformations;
- }
- /**
- * Executes each transformation.
- *
- * @return void
- */
- public function execute()
- {
- $xml = $this->getSource();
- if ($xml)
- {
- $behaviours = new DocBlox_Transformer_Behaviour_Collection(array(
- new DocBlox_Transformer_Behaviour_GeneratePaths(),
- new DocBlox_Transformer_Behaviour_AddLinkInformation(),
- new DocBlox_Transformer_Behaviour_Inherit(),
- ));
- $behaviours->setLogger(DocBlox_Core_Abstract::$logger);
- $xml = $behaviours->process($xml);
- }
- foreach ($this->getTransformations() as $transformation)
- {
- $this->log('Applying transformation query ' . $transformation->getQuery()
- . ' using writer ' . get_class($transformation->getWriter()));
- $transformation->execute($xml);
- }
- }
- /**
- * Converts a source file name to the name used for generating the end result.
- *
- * @param string $file
- *
- * @return string
- */
- public function generateFilename($file)
- {
- $info = pathinfo(str_replace(DIRECTORY_SEPARATOR, '_', trim($file, DIRECTORY_SEPARATOR . '.')));
- return '_' . $info['filename'] . '.html';
- }
- /**
- * Copies a file or folder recursively to another location.
- *
- * @param string $src The source location to copy
- * @param string $dst The destination location to copy to
- *
- * @throws Exception if $src does not exist or $dst is not writable
- *
- * @return void
- */
- public function copyRecursive($src, $dst)
- {
- // if $src is a normal file we can do a regular copy action
- if (is_file($src))
- {
- copy($src, $dst);
- return;
- }
- $dir = opendir($src);
- if (!$dir) {
- throw new Exception('Unable to locate path "' . $src . '"');
- }
- // check if the folder exists, otherwise create it
- if ((!file_exists($dst)) && (false === mkdir($dst))) {
- throw new Exception('Unable to create folder "' . $dst . '"');
- }
- while (false !== ($file = readdir($dir)))
- {
- if (($file != '.') && ($file != '..')) {
- if (is_dir($src . '/' . $file)) {
- $this->copyRecursive($src . '/' . $file, $dst . '/' . $file);
- }
- else
- {
- copy($src . '/' . $file, $dst . '/' . $file);
- }
- }
- }
- closedir($dir);
- }
- }