XmlFile.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\Migrations\Configuration\Migration;
  4. use Doctrine\Migrations\Configuration\Configuration;
  5. use Doctrine\Migrations\Configuration\Exception\FileNotFound;
  6. use Doctrine\Migrations\Configuration\Migration\Exception\XmlNotValid;
  7. use Doctrine\Migrations\Tools\BooleanStringFormatter;
  8. use DOMDocument;
  9. use SimpleXMLElement;
  10. use function assert;
  11. use function file_exists;
  12. use function file_get_contents;
  13. use function libxml_clear_errors;
  14. use function libxml_use_internal_errors;
  15. use function simplexml_load_string;
  16. use function strtr;
  17. use const DIRECTORY_SEPARATOR;
  18. use const LIBXML_NOCDATA;
  19. final class XmlFile extends ConfigurationFile
  20. {
  21. public function getConfiguration(): Configuration
  22. {
  23. if (! file_exists($this->file)) {
  24. throw FileNotFound::new($this->file);
  25. }
  26. $this->validateXml($this->file);
  27. $rawXML = file_get_contents($this->file);
  28. assert($rawXML !== false);
  29. $root = simplexml_load_string($rawXML, SimpleXMLElement::class, LIBXML_NOCDATA);
  30. assert($root !== false);
  31. $config = $this->extractParameters($root, true);
  32. if (isset($config['all_or_nothing'])) {
  33. $config['all_or_nothing'] = BooleanStringFormatter::toBoolean(
  34. $config['all_or_nothing'],
  35. false
  36. );
  37. }
  38. if (isset($config['migrations_paths'])) {
  39. $config['migrations_paths'] = $this->getDirectoriesRelativeToFile(
  40. $config['migrations_paths'],
  41. $this->file
  42. );
  43. }
  44. return (new ConfigurationArray($config))->getConfiguration();
  45. }
  46. /**
  47. * @return mixed[]
  48. */
  49. private function extractParameters(SimpleXMLElement $root, bool $loopOverNodes): array
  50. {
  51. $config = [];
  52. $itemsToCheck = $loopOverNodes ? $root->children() : $root->attributes();
  53. if (! ($itemsToCheck instanceof SimpleXMLElement)) {
  54. return $config;
  55. }
  56. foreach ($itemsToCheck as $node) {
  57. $nodeName = strtr($node->getName(), '-', '_');
  58. if ($nodeName === 'migrations_paths') {
  59. $config['migrations_paths'] = [];
  60. foreach ($node->{'path'} as $pathNode) {
  61. $config['migrations_paths'][(string) $pathNode['namespace']] = (string) $pathNode;
  62. }
  63. } elseif ($nodeName === 'storage' && $node->{'table-storage'} instanceof SimpleXMLElement) {
  64. $config['table_storage'] = $this->extractParameters($node->{'table-storage'}, false);
  65. } elseif ($nodeName === 'migrations') {
  66. $config['migrations'] = [];
  67. foreach ($node->{'migration'} as $pathNode) {
  68. $config['migrations'][] = (string) $pathNode;
  69. }
  70. } else {
  71. $config[$nodeName] = (string) $node;
  72. }
  73. }
  74. return $config;
  75. }
  76. private function validateXml(string $file): void
  77. {
  78. try {
  79. libxml_use_internal_errors(true);
  80. $xml = new DOMDocument();
  81. if ($xml->load($file) === false) {
  82. throw XmlNotValid::malformed();
  83. }
  84. $xsdPath = __DIR__ . DIRECTORY_SEPARATOR . 'XML' . DIRECTORY_SEPARATOR . 'configuration.xsd';
  85. if ($xml->schemaValidate($xsdPath) === false) {
  86. throw XmlNotValid::failedValidation();
  87. }
  88. } finally {
  89. libxml_clear_errors();
  90. libxml_use_internal_errors(false);
  91. }
  92. }
  93. }