/** * Adds extra information to the structure. * * This method enhances the Structure information with the following information: * - Every file receives a 'generated-path' attribute which contains the path on the filesystem where the docs for * that file van be found. * * @param DOMDocument $xml * * @return DOMDocument */ public function process(DOMDocument $xml) { if ($this->logger) { $this->logger->log('Adding path information to each xml "file" tag'); } $xpath = new DOMXPath($xml); $qry = $xpath->query("/project/file[@path]"); /** @var DOMElement $element */ foreach ($qry as $element) { $files[] = $element->getAttribute('path'); $element->setAttribute('generated-path', $this->generateFilename($element->getAttribute('path'))); } return $xml; }
/** * Removes DocBlocks marked with 'ignore' tag from the structure * * @param DOMDocument $xml * * @return DOMDocument */ public function process(DOMDocument $xml) { if ($this->logger) { $this->logger->log('Removing DocBlocks containing the @' . $this->tag . ' tag'); } $ignoreQry = '//tag[@name=\'' . $this->tag . '\']'; $xpath = new DOMXPath($xml); $nodes = $xpath->query($ignoreQry); /** @var DOMElement $node */ foreach ($nodes as $node) { $remove = $node->parentNode->parentNode; $node->parentNode->parentNode->parentNode->removeChild($remove); } return $xml; }
/** * Apply inheritance of docblock elements to all elements. * * Apply the inheritance rules from root node to edge leaf; this way the * inheritance cascades. * * Note: the process below must _first_ be done on interfaces and a second * pass on classes. If this is not done then not everything will be picked * up because you effectively have 2 separate sets of root nodes. * * This does mean that an interface will populate any class in which it is * implemented but will not walk further down the tree. * * Interfaces do not check whether they implement another interface because * interfaces do not support the IMPLEMENTS keyword. * * Actions: * * 1. Get root nodes with present leafs * 2. Get Extended/implemented leafs * 3. If SD misses for leaf; apply SD of root * 4. If LD misses for leaf; apply LD of root * 5. if LD of leaf contains {@inheritdoc}; replace with LD of root * 6. if @category of leaf is missing; use @category of root * 7. if @package of leaf is missing; use @package of root * 8. if @subcategory of leaf is missing; use @subpackage of root * 9. if @version of leaf is missing; use @version of root * 10. if @copyright of leaf is missing; use @copyright of root * 11. if @author of leaf is missing; use @author of root * * 12. If root and leaf share a method with the same name: * 13. If SD misses for leaf method; apply SD of root method * 14. If LD misses for leaf method; apply LD of root method * 15. if LD of leaf method contains {@inheritdoc}; replace with LD of root method * 16. if @params of leaf method is missing; use @params of root method * 17. if @return of leaf method is missing; use @return of root method * 18. if @throw/throws of leaf method is missing; use @throws/throw of root method * 19. if @version of leaf method is missing; use @version of root method * 20. if @copyright of leaf method is missing; use @copyright of root method * 21. if @author of leaf method is missing; use @author of root method * * 22. If root and leaf share a property with the same name: * 23. If SD misses for leaf property; apply SD of root property * 24. If LD misses for leaf property; apply LD of root property * 25. if LD of leaf property contains {@inheritdoc}; replace with LD of root property * 26. if @var of leaf property is missing; use @var of root property * 27. if @version of leaf property is missing; use @version of root property * 28. if @copyright of leaf property is missing; use @copyright of root property * 29. if @author of leaf property is missing; use @author of root property * * @param DOMDocument $xml * * @return DOMDocument */ public function process(DOMDocument $xml) { if ($this->logger) { $this->logger->log('Adding path information to each xml "file" tag'); } $xpath = new DOMXPath($xml); // get all interfaces that do not extend from anything or whose extend // is not featured in this project; these are considered root nodes. /** @var DOMElement[] $result */ $result = $xpath->query('/project/file/interface[extends=""]' . '|/project/file/interface[not(extends = /project/file/class/full_name)]'); foreach ($result as $node) { $inherit = new DocBlox_Transformer_Behaviour_Inherit_Node_Interface($node, $xpath); $super = array('classes' => array(), 'properties' => array(), 'methods' => array()); $inherit->apply($super, null); } // get all classes that do not extend from anything or whose extend // is not featured in this project; these are considered root nodes. /** @var DOMElement[] $result */ $result = $xpath->query('/project/file/class[extends=""]' . '|/project/file/class[not(extends = /project/file/class/full_name)]'); foreach ($result as $node) { $inherit = new DocBlox_Transformer_Behaviour_Inherit_Node_Class($node, $xpath); $super = array('classes' => array(), 'properties' => array(), 'methods' => array()); $inherit->apply($super, null); } return $xml; }
/** * Find all return tags that contain 'self' or '$this' and replace those * terms for the name of the current class' type. * * @param DOMDocument $xml * * @return DOMDocument */ public function process(DOMDocument $xml) { if ($this->logger) { $this->logger->log('Transforming `self` and `$this` statements for @return tags'); } $ignoreQry = '//tag[@name=\'return\' and @type=\'\\self\']' . '|//tag[@name=\'return\' and @type=\'\\$this\']' . '|//tag[@name=\'return\']/type[.=\'\\self\']' . '|//tag[@name=\'return\']/type[.=\'\\$this\']'; $xpath = new DOMXPath($xml); $nodes = $xpath->query($ignoreQry); /** @var DOMElement $node */ foreach ($nodes as $node) { // if a node with name 'type' is selected we need to reach one // level further. $docblock = $node->nodeName == 'type' ? $node->parentNode->parentNode : $node->parentNode; $method = $docblock->parentNode; // find the name of the method $method_name = $method->getElementsByTagName('name')->item(0)->nodeValue; // if the method is not a method but a global function: error! if ($method->nodeName != 'method') { $this->logger->log('Global function ' . $method_name . ' contains a reference ' . 'to self or $self', DocBlox_Core_Log::ERR); continue; } $type = $method->parentNode->getElementsByTagName('full_name')->item(0)->nodeValue; // nodes with name type need to set their content; otherwise we set // an attribute of the class itself if ($node->nodeName == 'type') { $node->nodeValue = $type; // add a new tag @fluent to indicate that this is a fluent interface // we only add it here since there should always be a node `type` $fluent_tag = new DOMElement('tag'); $docblock->appendChild($fluent_tag); $fluent_tag->setAttribute('name', 'fluent'); $fluent_tag->setAttribute('description', 'This method is part of a fluent interface and will return ' . 'the same instance'); } else { $node->setAttribute('type', $type); } // check if an excerpt is set and override that as well if ($node->hasAttribute('excerpt') && ($node->getAttribute('excerpt') == '\\self' || $node->getAttribute('excerpt') == '\\$this')) { $node->setAttribute('excerpt', $type); } } return $xml; }
public function testLog() { if (file_exists('/tmp/DocBlox_Core_Log_test')) { unlink('/tmp/DocBlox_Core_Log_test'); } $this->fixture = new DocBlox_Core_Log('/tmp/DocBlox_Core_Log_test'); $this->fixture->setThreshold(DocBlox_Core_Log::ERR); $this->fixture->log('test', DocBlox_Core_Log::ERR); $this->fixture->log('test2', DocBlox_Core_Log::INFO); $result = file_get_contents('/tmp/DocBlox_Core_Log_test'); $this->assertNotEmpty($result); $this->assertContains('test', $result); $this->assertNotContains('mb]:', $result, 'Should not contain debug information'); $this->assertNotContains('test2', $result, 'Should not contain test2 as it is of a lower level'); $this->fixture->setThreshold(DocBlox_Core_Log::DEBUG); $this->fixture->log('test3', DocBlox_Core_Log::INFO); $result = file_get_contents('/tmp/DocBlox_Core_Log_test'); $this->assertContains('test3', $result); $this->assertContains('mb]:', $result, 'Should contain debug information when threshold is DEBUG'); $this->fixture->log(array('test4'), DocBlox_Core_Log::INFO); $result = file_get_contents('/tmp/DocBlox_Core_Log_test'); $this->assertContains('array', $result, 'The log should contain a var_dumped output'); }
/** * Logs the message to the log with the given priority. * * This method only works if the Log Level is higher than the given priority. * If there is no logger object than this method will instantiate it. * In contrary to the debug statement this only logs strings. * * @param string $message Element to log. * @param int $priority Priority of the given log. * * @see DocBlock_Abstract::setLogLevel() * @see Zend_Log * * @return void */ public function log($message, $priority = DocBlox_Core_Log::INFO) { if ($priority == DocBlox_Core_Log::DEBUG) { $this->debug($message); return; } if (!self::$logger || !self::$stdout_logger) { $config = $this->getConfig(); // log to file self::$logger = new DocBlox_Core_Log($config->logging->paths->default); self::$logger->setThreshold($this->getLogLevel()); // log to stdout self::$stdout_logger = new DocBlox_Core_Log(DocBlox_Core_Log::FILE_STDOUT); self::$stdout_logger->setThreshold($this->getLogLevel()); } self::$logger->log($message, $priority); self::$stdout_logger->log($message, $priority); }
/** * Logs the given to a debug log. * * If anything other than a string is passed than the item is var_dumped * and then stored. * * @param string|array|object $message Item to log. * * @return void */ public function log($message) { $this->logger->log($message, DocBlox_Core_Log::DEBUG); }
/** * Adds extra information to the structure. * * This method enhances the Structure information with the following information: * - Every @see tag, or a tag with a type receives an attribute with a direct link to that tag's type entry. * - Every tag receives an excerpt containing the first 15 characters. * * @param DOMDocument $xml * * @return DOMDocument */ public function process(DOMDocument $xml) { if ($this->logger) { $this->logger->log('Adding path information to each xml "file" tag'); } $xpath = new DOMXPath($xml); // add to classes $qry = $xpath->query('//class[full_name]/..'); $class_paths = array(); /** @var DOMElement $element */ foreach ($qry as $element) { $path = $element->getAttribute('path'); foreach ($element->getElementsByTagName('class') as $class) { $class_paths[$class->getElementsByTagName('full_name')->item(0)->nodeValue] = $path; } } // add to interfaces $qry = $xpath->query('//interface[full_name]/..'); /** @var DOMElement $element */ foreach ($qry as $element) { $path = $element->getAttribute('path'); /** @var DOMElement $class */ foreach ($element->getElementsByTagName('interface') as $class) { $class_paths[$class->getElementsByTagName('full_name')->item(0)->nodeValue] = $path; } } // add extra xml elements to tags if ($this->logger) { $this->logger->log('Adding link information and excerpts to all DocBlock tags'); } $qry = $xpath->query('//docblock/tag/@type|//docblock/tag/type|//extends|//implements'); $declared_classes = get_declared_classes(); /** @var DOMElement $element */ foreach ($qry as $element) { $type = rtrim($element->nodeValue, '[]'); $node = $element->nodeType == XML_ATTRIBUTE_NODE ? $element->parentNode : $element; // if the class is already loaded and is an internal class; refer // to the PHP man pages if (in_array(ltrim($type, '\\'), $declared_classes)) { $refl = new ReflectionClass($type); if ($refl->isInternal()) { $node->setAttribute('link', 'http://php.net/manual/en/class.' . strtolower(ltrim($type, '\\')) . '.php'); } } if (isset($class_paths[$type])) { $file_name = $this->generateFilename($class_paths[$type]); $node->setAttribute('link', $file_name . '#' . $type); } // add a 15 character excerpt of the node contents, meant for the sidebar $node->setAttribute('excerpt', utf8_encode(substr($type, 0, 15) . (strlen($type) > 15 ? '...' : ''))); } // convert class names to links // this action also checks the link of an @link tag it it starts with // `http://`, `https://` or `www.`. if not: also convert those. $qry = $xpath->query('//docblock/tag[@name="see" or @name="throw" or @name="throws"]' . '|//docblock/tag[@name="link" ' . 'and (substring(@link,1,7) != \'http://\' ' . 'or substring(@link,1,4) != \'www.\'' . 'or substring(@link,1,7) != \'https://\')]'); /** @var DOMElement $element */ foreach ($qry as $element) { $name = $element->getAttribute('name') == 'link' ? $element->getAttribute('link') : $element->nodeValue; $node_value = explode('::', $name); if (isset($class_paths[$node_value[0]])) { $file_name = $this->generateFilename($class_paths[$node_value[0]]); $element->setAttribute('link', $file_name . '#' . $name); } } return $xml; }