PageRenderTime 26ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/packages/psdContentClassDefinition.php

https://gitlab.com/codinger/psdcontentclassimport
PHP | 448 lines | 181 code | 121 blank | 146 comment | 29 complexity | 74db1d8aff09bdfec399578dd2d47456 MD5 | raw file
  1. <?php
  2. /**
  3. * Represents a wrapper for text-based content-class definitions (manifested by a class-file.xml inside of a package-
  4. * folder).
  5. *
  6. * @author Oliver Erdmann, o.erdmann@finaldream.de
  7. */
  8. class psdContentClassDefinition
  9. {
  10. /**
  11. * Filename of content-class definition.
  12. *
  13. * @var string
  14. */
  15. protected $fileName = '';
  16. /**
  17. * Class Identifier from XML-File.
  18. *
  19. * @var string
  20. */
  21. protected $classIdentifier = '';
  22. /**
  23. * Set true in order to output statistics.
  24. *
  25. * @var boolean
  26. */
  27. protected $verbose = false;
  28. /**
  29. * Commanline interface for console output.
  30. *
  31. * @var eZCLI|null
  32. */
  33. protected $cli = null;
  34. /**
  35. * Implies load().
  36. *
  37. * @param string $fileName Filename to load.
  38. * @param boolean $verbose Make cli output or not.
  39. */
  40. public function __construct($fileName, $verbose = false)
  41. {
  42. $this->cli = eZCLI::instance();
  43. $this->verbose = $verbose;
  44. $this->load($fileName);
  45. }
  46. /**
  47. * Sets the context for a certain file.
  48. *
  49. * @param string $fileName File to further process.
  50. *
  51. * @throws Exception If file does not exists.
  52. *
  53. * @return void
  54. */
  55. public function load($fileName)
  56. {
  57. if (file_exists($fileName) === false) {
  58. throw new Exception(sprintf('File %s not found!', $fileName));
  59. }
  60. $this->fileName = $fileName;
  61. $this->loadClassInfo();
  62. }
  63. /**
  64. * Returns the class-identifier.
  65. *
  66. * @return string
  67. */
  68. public function getClassIdentifier()
  69. {
  70. return $this->classIdentifier;
  71. }
  72. /**
  73. * Applies some transformations to the XML with the goal to make the code a bit easier to edit.
  74. *
  75. * Requires a preceding call of load() in order to operate on something.
  76. * The transformations are:
  77. * - converting all serialized fields to JSON
  78. * - setting a new create- and modified-date
  79. * - normalize the attribute-placement
  80. * - add attribute comments.
  81. *
  82. * @return void
  83. */
  84. public function transformXML()
  85. {
  86. $dom = $this->openXMLFile($this->fileName);
  87. $this->updateSerializedFields($dom);
  88. $this->updateCreatedFromDOM($dom);
  89. $this->updateModifiedFromDOM($dom);
  90. $this->normalizePlacement($dom);
  91. $this->addAttributeInfoComments($dom);
  92. $dom->save($this->fileName);
  93. }
  94. /**
  95. * Opens an XML-file and returns the resulting DOMDocument.
  96. *
  97. * @param string $fileName The filename.
  98. *
  99. * @throws Exception If XML could not be parsed.
  100. *
  101. * @return DOMDocument
  102. */
  103. public function openXMLFile($fileName)
  104. {
  105. $dom = new DOMDocument('1.0', 'utf-8');
  106. $dom->preserveWhiteSpace = false;
  107. $dom->formatOutput = true;
  108. $success = $dom->load($fileName);
  109. $this->logLine('Open XML file: '.$fileName, __METHOD__);
  110. if (!$success) {
  111. throw new Exception('Not an XML-Document! '.$fileName);
  112. }
  113. return $dom;
  114. }
  115. /**
  116. * Reads basic information on a class from the XML-definition.
  117. *
  118. * @return void
  119. */
  120. protected function loadClassInfo()
  121. {
  122. $dom = $this->openXMLFile($this->fileName);
  123. $xPath = new DOMXPath($dom);
  124. $node = $xPath->query(psdPackage::XPATH_IDENTIFIER);
  125. if ($node->length > 0) {
  126. $this->classIdentifier = $node->item(0)->nodeValue;
  127. }
  128. }
  129. /**
  130. * Transforms searialized Fields from PHP-Serialize to JSON. Recodes entities.
  131. *
  132. * @param DOMDocument $dom DOM to transform.
  133. *
  134. * @return void
  135. */
  136. public function updateSerializedFields(DOMDocument $dom)
  137. {
  138. $xPath = new DOMXPath($dom);
  139. // Select all nodes with names that start with "serialized-".
  140. $elements = $xPath->query('//*[starts-with(name(), "serialized-")]');
  141. if (!($elements instanceof DOMNodeList)) {
  142. return;
  143. }
  144. $this->logLine('Transforming '.$elements->length.' elements in '.$this->fileName, __METHOD__);
  145. foreach ($elements as $element) {
  146. if (!empty($element->textContent)) {
  147. $val = $this->reSerializeString($element->textContent);
  148. $element->nodeValue = htmlentities($val, ENT_NOQUOTES, 'UTF-8');
  149. }
  150. }
  151. }
  152. /**
  153. * Updates the modified-field of a given DOM-Structure. Requires the DOM to be a Content-Class Definition.
  154. *
  155. * @param DOMDocument $dom The DOM Document.
  156. * @param int $timeStamp The unix timestamp.
  157. *
  158. * @return void
  159. */
  160. public function updateModifiedFromDOM(DOMDocument $dom, $timeStamp = 0)
  161. {
  162. $xPath = new DOMXPath($dom);
  163. $nodes = $xPath->query(psdPackage::XPATH_MODIFIED);
  164. if (!($nodes instanceof DOMNodeList) || $nodes->length < 1) {
  165. return;
  166. }
  167. if (empty($timeStamp)) {
  168. $timeStamp = time();
  169. }
  170. $nodes->item(0)->nodeValue = $timeStamp;
  171. $this->logLine(sprintf('New "Modified" timestamp: %s', $timeStamp), __METHOD__);
  172. }
  173. /**
  174. * Updates the modified-field for the current file. Requires a call of load().
  175. *
  176. * @param int $timeStamp The unix timestamp.
  177. *
  178. * @return void
  179. */
  180. public function updateModified($timeStamp = 0)
  181. {
  182. $dom = $this->openXMLFile($this->fileName);
  183. $this->logLine('Update modified-date for '.$this->fileName, __METHOD__);
  184. $this->updateModifiedFromDOM($dom, $timeStamp);
  185. $this->normalizePlacement($dom);
  186. $this->addAttributeInfoComments($dom);
  187. $dom->save($this->fileName);
  188. }
  189. /**
  190. * Updates the created-field of a given DOM-Structure. Requires the DOM to be a Content-Class Definition.
  191. *
  192. * @param DOMDocument $dom The DOM Document.
  193. * @param int $timeStamp The unix timestamp.
  194. *
  195. * @return void
  196. */
  197. public function updateCreatedFromDOM(DOMDocument $dom, $timeStamp = 0)
  198. {
  199. $xPath = new DOMXPath($dom);
  200. $nodes = $xPath->query(psdPackage::XPATH_MODIFIED);
  201. if (!($nodes instanceof DOMNodeList) || $nodes->length < 1) {
  202. return;
  203. }
  204. if (empty($timeStamp)) {
  205. $timeStamp = time();
  206. }
  207. $nodes->item(0)->nodeValue = $timeStamp;
  208. $this->logLine(sprintf('New "Created" timestamp: %d', $timeStamp), __METHOD__);
  209. }
  210. /**
  211. * Updates the modified-field for the current file. Requires a call of load().
  212. *
  213. * @param int $timeStamp The unix timestamp.
  214. *
  215. * @return void
  216. */
  217. public function updateCreated($timeStamp = 0)
  218. {
  219. $dom = $this->openXMLFile($this->fileName);
  220. $this->logLine('Update created-date for '.$this->fileName, __METHOD__);
  221. $this->updateCreatedFromDOM($dom, $timeStamp);
  222. $dom->save($this->fileName);
  223. }
  224. /**
  225. * Loops through all placement-tags and undates the numbering to an linear sequence.
  226. *
  227. * @param DOMDocument $dom The DOM Document.
  228. *
  229. * @return void.
  230. */
  231. public function normalizePlacement(DOMDocument $dom)
  232. {
  233. $xPath = new DOMXPath($dom);
  234. $namespace = $dom->lookupNamespaceUri('ezcontentclass-attri');
  235. $xPath->registerNamespace('ezcontentclass-attri', $namespace);
  236. $nodes = $xPath->query(psdPackage::XPATH_PLACEMENT);
  237. if (!($nodes instanceof DOMNodeList) || $nodes->length < 1) {
  238. return;
  239. }
  240. $this->logLine('Update placement for: '.$nodes->length.' nodes.', __METHOD__);
  241. for ($i = 0, $j = $nodes->length; $i < $j; $i++) {
  242. $nodes->item($i)->nodeValue = $i + 1;
  243. }
  244. }
  245. /**
  246. * Inserts comments containing the attribute-identifier before each attribute-node.
  247. * This is done in order to find attributes more easily.
  248. *
  249. * @param DOMDocument $dom Dom to transform.
  250. *
  251. * @return void
  252. */
  253. public function addAttributeInfoComments(DOMDocument $dom)
  254. {
  255. $xPath = new DOMXPath($dom);
  256. $namespace = $dom->lookupNamespaceUri('ezcontentclass-attri');
  257. $xPath->registerNamespace('ezcontentclass-attri', $namespace);
  258. $nodes = $xPath->query(psdPackage::XPATH_ATTRIBUTES);
  259. if (!($nodes instanceof DOMNodeList) || $nodes->length < 1) {
  260. return;
  261. }
  262. // Loop the ezcontentclass-attri:attributes nodes.
  263. for ($i = 0, $il = $nodes->length; $i < $il; $i++) {
  264. $attributes = $nodes->item($i);
  265. if (!($attributes instanceof DOMNode) || $attributes->childNodes->length < 1) {
  266. continue;
  267. }
  268. $children = array();
  269. // Cache the attributes in order to prevent the loop from being modified.
  270. for ($j = 0, $jl = $attributes->childNodes->length; $j < $jl; $j++) {
  271. $child = $attributes->childNodes->item($j);
  272. if ($child instanceof DOMNode && $child->nodeName == 'attribute') {
  273. $children[] = $child;
  274. }
  275. }
  276. // Loop the attribute-nodes.
  277. foreach ($children as $child) {
  278. // If the current attribute-node is preceded by a comment, remove it first.
  279. if ($child->previousSibling instanceof DOMComment) {
  280. $attributes->removeChild($child->previousSibling);
  281. }
  282. // Get the identifier.
  283. $identifier = $xPath->query('identifier', $child);
  284. if (!($identifier instanceof DOMNodeList) || $identifier->length < 1) {
  285. continue;
  286. }
  287. $commentValue = sprintf(' %s ', $identifier->item(0)->nodeValue);
  288. $comment = new DOMComment($commentValue);
  289. // Add the generated comment before the attribute.
  290. $attributes->insertBefore($comment, $child);
  291. }//end foreach
  292. }//end for
  293. }
  294. /**
  295. * Tries to change a serialized string to JSON. If the input is not serialized, it's returned as is.
  296. *
  297. * @param string $str Serialized string.
  298. *
  299. * @return mixed|string
  300. */
  301. public function reSerializeString($str)
  302. {
  303. $result = @unserialize($str);
  304. if ($result === false) {
  305. $result = $str;
  306. } else {
  307. $result = json_encode($result);
  308. }
  309. return $result;
  310. }
  311. /**
  312. * Writes a line to the console if $verbose is enabled.
  313. *
  314. * @param string $str Message to be written.
  315. * @param string $method Optional Method name, only used for debug-log.
  316. *
  317. * @return void
  318. */
  319. public function logLine($str, $method = '')
  320. {
  321. eZDebug::writeNotice('*'.__CLASS__.': '.$str, $method);
  322. if (!$this->verbose) {
  323. return;
  324. }
  325. $this->cli->output($str, true);
  326. }
  327. }