PageRenderTime 25ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/phpdocumentor/phpdocumentor/src/phpDocumentor/Plugin/Core/Transformer/Writer/Xsl.php

https://gitlab.com/faisaliqbal/mytripsorter
PHP | 361 lines | 204 code | 42 blank | 115 comment | 28 complexity | 41b515e539f7a62fbe8d26535c53011e MD5 | raw file
  1. <?php
  2. /**
  3. * phpDocumentor
  4. *
  5. * PHP Version 5.3
  6. *
  7. * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
  8. * @license http://www.opensource.org/licenses/mit-license.php MIT
  9. * @link http://phpdoc.org
  10. */
  11. namespace phpDocumentor\Plugin\Core\Transformer\Writer;
  12. use Monolog\Logger;
  13. use phpDocumentor\Application;
  14. use phpDocumentor\Descriptor\ProjectDescriptor;
  15. use phpDocumentor\Event\Dispatcher;
  16. use phpDocumentor\Plugin\Core\Exception;
  17. use phpDocumentor\Transformer\Event\PreXslWriterEvent;
  18. use phpDocumentor\Transformer\Router\ForFileProxy;
  19. use phpDocumentor\Transformer\Router\Queue;
  20. use phpDocumentor\Transformer\Transformation;
  21. use phpDocumentor\Transformer\Transformation as TransformationObject;
  22. use phpDocumentor\Transformer\Writer\Exception\RequirementMissing;
  23. use phpDocumentor\Transformer\Writer\Routable;
  24. use phpDocumentor\Transformer\Writer\WriterAbstract;
  25. /**
  26. * XSL transformation writer; generates static HTML out of the structure and XSL templates.
  27. */
  28. class Xsl extends WriterAbstract implements Routable
  29. {
  30. /** @var \Monolog\Logger $logger */
  31. protected $logger;
  32. /** @var string[] */
  33. protected $xsl_variables = array();
  34. /** @var Queue */
  35. private $routers;
  36. /**
  37. * Initialize this writer with the logger so that it can output logs.
  38. *
  39. * @param Logger $logger
  40. */
  41. public function __construct(Logger $logger)
  42. {
  43. $this->logger = $logger;
  44. }
  45. /**
  46. * Checks whether XSL handling is enabled with PHP as that is not enabled by default.
  47. *
  48. * To enable XSL handling you need either the xsl extension or the xslcache extension installed.
  49. *
  50. * @throws RequirementMissing if neither xsl extensions are installed.
  51. *
  52. * @return void
  53. */
  54. public function checkRequirements()
  55. {
  56. if (!class_exists('XSLTProcessor') && (!extension_loaded('xslcache'))) {
  57. throw new RequirementMissing(
  58. 'The XSL writer was unable to find your XSLTProcessor; '
  59. . 'please check if you have installed the PHP XSL extension or XSLCache extension'
  60. );
  61. }
  62. }
  63. /**
  64. * Sets the routers that can be used to determine the path of links.
  65. *
  66. * @param Queue $routers
  67. *
  68. * @return void
  69. */
  70. public function setRouters(Queue $routers)
  71. {
  72. $this->routers = $routers;
  73. }
  74. /**
  75. * This method combines the structure.xml and the given target template
  76. * and creates a static html page at the artifact location.
  77. *
  78. * @param ProjectDescriptor $project Document containing the structure.
  79. * @param Transformation $transformation Transformation to execute.
  80. *
  81. * @throws \RuntimeException if the structure.xml file could not be found.
  82. * @throws Exception if the structure.xml file's documentRoot could not be read because of encoding issues
  83. * or because it was absent.
  84. *
  85. * @return void
  86. */
  87. public function transform(ProjectDescriptor $project, Transformation $transformation)
  88. {
  89. $structure = $this->loadAst($this->getAstPath($transformation));
  90. $proc = $this->getXslProcessor($transformation);
  91. $proc->registerPHPFunctions();
  92. $this->registerDefaultVariables($transformation, $proc, $structure);
  93. $this->setProcessorParameters($transformation, $proc);
  94. $artifact = $this->getArtifactPath($transformation);
  95. $this->checkForSpacesInPath($artifact);
  96. // if a query is given, then apply a transformation to the artifact
  97. // location by replacing ($<var>} with the sluggified node-value of the
  98. // search result
  99. if ($transformation->getQuery() !== '') {
  100. $xpath = new \DOMXPath($structure);
  101. /** @var \DOMNodeList $qry */
  102. $qry = $xpath->query($transformation->getQuery());
  103. $count = $qry->length;
  104. foreach ($qry as $key => $element) {
  105. Dispatcher::getInstance()->dispatch(
  106. 'transformer.writer.xsl.pre',
  107. PreXslWriterEvent::createInstance($this)->setElement($element)->setProgress(array($key+1, $count))
  108. );
  109. $proc->setParameter('', $element->nodeName, $element->nodeValue);
  110. $file_name = $transformation->getTransformer()->generateFilename(
  111. $element->nodeValue
  112. );
  113. if (! $artifact) {
  114. $url = $this->generateUrlForXmlElement($project, $element);
  115. if ($url === false || $url[0] !== DIRECTORY_SEPARATOR) {
  116. continue;
  117. }
  118. $filename = $transformation->getTransformer()->getTarget()
  119. . str_replace('/', DIRECTORY_SEPARATOR, $url);
  120. } else {
  121. $filename = str_replace('{$' . $element->nodeName . '}', $file_name, $artifact);
  122. }
  123. $relativeFileName = substr($filename, strlen($transformation->getTransformer()->getTarget()) + 1);
  124. $proc->setParameter('', 'root', str_repeat('../', substr_count($relativeFileName, '/')));
  125. $this->writeToFile($filename, $proc, $structure);
  126. }
  127. } else {
  128. if (substr($transformation->getArtifact(), 0, 1) == '$') {
  129. // not a file, it must become a variable!
  130. $variable_name = substr($transformation->getArtifact(), 1);
  131. $this->xsl_variables[$variable_name] = $proc->transformToXml($structure);
  132. } else {
  133. $relativeFileName = substr($artifact, strlen($transformation->getTransformer()->getTarget()) + 1);
  134. $proc->setParameter('', 'root', str_repeat('../', substr_count($relativeFileName, '/')));
  135. $this->writeToFile($artifact, $proc, $structure);
  136. }
  137. }
  138. }
  139. /**
  140. * Takes the filename and converts it into a correct URI for XSLTProcessor.
  141. *
  142. * @param string $filename
  143. *
  144. * @return string
  145. */
  146. protected function getXsltUriFromFilename($filename)
  147. {
  148. // Windows requires an additional / after the scheme. If not provided then errno 22 (I/O Error: Invalid
  149. // Argument) will be raised. Thanks to @FnTmLV for finding the cause. See issue #284 for more information.
  150. // An exception to the above is when running from a Phar file; in this case the stream is handled as if on
  151. // linux; see issue #713 for more information on this exception.
  152. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && ! \Phar::running()) {
  153. $filename = '/' . $filename;
  154. }
  155. return 'file://' . $filename;
  156. }
  157. /**
  158. * Sets the parameters of the XSLT processor.
  159. *
  160. * @param TransformationObject $transformation Transformation.
  161. * @param \XSLTProcessor $proc XSLTProcessor.
  162. *
  163. * @return void
  164. */
  165. public function setProcessorParameters(TransformationObject $transformation, $proc)
  166. {
  167. foreach ($this->xsl_variables as $key => $variable) {
  168. // XSL does not allow both single and double quotes in a string
  169. if ((strpos($variable, '"') !== false)
  170. && ((strpos($variable, "'") !== false))
  171. ) {
  172. $this->logger->warning(
  173. 'XSLT does not allow both double and single quotes in '
  174. . 'a variable; transforming single quotes to a character '
  175. . 'encoded version in variable: ' . $key
  176. );
  177. $variable = str_replace("'", "&#39;", $variable);
  178. }
  179. $proc->setParameter('', $key, $variable);
  180. }
  181. // add / overwrite the parameters with those defined in the
  182. // transformation entry
  183. $parameters = $transformation->getParameters();
  184. if (isset($parameters['variables'])) {
  185. /** @var \DOMElement $variable */
  186. foreach ($parameters['variables'] as $key => $value) {
  187. $proc->setParameter('', $key, $value);
  188. }
  189. }
  190. }
  191. /**
  192. *
  193. *
  194. * @param Transformation $transformation
  195. *
  196. * @return \XSLTCache|\XSLTProcessor
  197. */
  198. protected function getXslProcessor(Transformation $transformation)
  199. {
  200. $xslTemplatePath = $transformation->getSourceAsPath();
  201. $this->logger->debug('Loading XSL template: ' . $xslTemplatePath);
  202. if (!file_exists($xslTemplatePath)) {
  203. throw new Exception('Unable to find XSL template "' . $xslTemplatePath . '"');
  204. }
  205. if (extension_loaded('xslcache')) {
  206. $proc = new \XSLTCache();
  207. $proc->importStyleSheet($xslTemplatePath, true);
  208. return $proc;
  209. } else {
  210. $xsl = new \DOMDocument();
  211. $xsl->load($xslTemplatePath);
  212. $proc = new \XSLTProcessor();
  213. $proc->importStyleSheet($xsl);
  214. return $proc;
  215. }
  216. }
  217. /**
  218. * @param $structureFilename
  219. * @return \DOMDocument
  220. */
  221. private function loadAst($structureFilename)
  222. {
  223. if (!is_readable($structureFilename)) {
  224. throw new \RuntimeException(
  225. 'Structure.xml file was not found in the target directory, is the XML writer missing from the '
  226. . 'template definition?'
  227. );
  228. }
  229. $structure = new \DOMDocument('1.0', 'utf-8');
  230. libxml_use_internal_errors(true);
  231. $structure->load($structureFilename);
  232. if (empty($structure->documentElement)) {
  233. $message = 'Specified DOMDocument lacks documentElement, cannot transform.';
  234. $error = libxml_get_last_error();
  235. if ($error) {
  236. $message .= PHP_EOL . 'Apparently an error occurred with reading the structure.xml file, the reported '
  237. . 'error was "' . trim($error->message) . '" on line ' . $error->line;
  238. }
  239. throw new Exception($message);
  240. }
  241. return $structure;
  242. }
  243. /**
  244. * @param Transformation $transformation
  245. * @param $proc
  246. * @param $structure
  247. */
  248. private function registerDefaultVariables(Transformation $transformation, $proc, $structure)
  249. {
  250. $proc->setParameter('', 'title', $structure->documentElement->getAttribute('title'));
  251. if ($transformation->getParameter('search') !== null && $transformation->getParameter('search')->getValue()) {
  252. $proc->setParameter('', 'search_template', $transformation->getParameter('search')->getValue());
  253. } else {
  254. $proc->setParameter('', 'search_template', 'none');
  255. }
  256. $proc->setParameter('', 'version', Application::$VERSION);
  257. $proc->setParameter('', 'generated_datetime', date('r'));
  258. }
  259. /**
  260. * @param $filename
  261. * @param $proc
  262. * @param $structure
  263. */
  264. private function writeToFile($filename, $proc, $structure)
  265. {
  266. if (!file_exists(dirname($filename))) {
  267. mkdir(dirname($filename), 0755, true);
  268. }
  269. $proc->transformToURI($structure, $this->getXsltUriFromFilename($filename));
  270. }
  271. /**
  272. * @param Transformation $transformation
  273. * @return string
  274. */
  275. private function getAstPath(Transformation $transformation)
  276. {
  277. return $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . 'structure.xml';
  278. }
  279. /**
  280. * Returns the path to the location where the artifact should be written, or null to automatically detect the
  281. * location using the router.
  282. *
  283. * @param Transformation $transformation
  284. *
  285. * @return string|null
  286. */
  287. private function getArtifactPath(Transformation $transformation)
  288. {
  289. return $transformation->getArtifact()
  290. ? $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . $transformation->getArtifact()
  291. : null;
  292. }
  293. /**
  294. * @param ProjectDescriptor $project
  295. * @param $element
  296. * @return false|string
  297. */
  298. private function generateUrlForXmlElement(ProjectDescriptor $project, $element)
  299. {
  300. $elements = $project->getIndexes()->get('elements');
  301. $elementFqcn = ($element->parentNode->nodeName === 'namespace' ? '~\\' : '') . $element->nodeValue;
  302. $node = (isset($elements[$elementFqcn]))
  303. ? $elements[$elementFqcn]
  304. : $element->nodeValue; // do not use the normalized version if the element is not found!
  305. $rule = $this->routers->match($node);
  306. if (!$rule) {
  307. throw new \InvalidArgumentException(
  308. 'No matching routing rule could be found for the given node, please provide an artifact location, '
  309. . 'encountered: ' . ($node === null ? 'NULL' : get_class($node))
  310. );
  311. }
  312. $rule = new ForFileProxy($rule);
  313. $url = $rule->generate($node);
  314. return $url;
  315. }
  316. }