YamlPecl.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. <?php
  2. namespace Drupal\Component\Serialization;
  3. use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
  4. /**
  5. * Provides default serialization for YAML using the PECL extension.
  6. */
  7. class YamlPecl implements SerializationInterface {
  8. /**
  9. * {@inheritdoc}
  10. */
  11. public static function encode($data) {
  12. static $init;
  13. if (!isset($init)) {
  14. ini_set('yaml.output_indent', 2);
  15. // Do not break lines at 80 characters.
  16. ini_set('yaml.output_width', -1);
  17. $init = TRUE;
  18. }
  19. return yaml_emit($data, YAML_UTF8_ENCODING, YAML_LN_BREAK);
  20. }
  21. /**
  22. * {@inheritdoc}
  23. */
  24. public static function decode($raw) {
  25. static $init;
  26. if (!isset($init)) {
  27. // Decode binary, since Symfony YAML parser encodes binary from 3.1
  28. // onwards.
  29. ini_set('yaml.decode_binary', 1);
  30. // We never want to unserialize !php/object.
  31. ini_set('yaml.decode_php', 0);
  32. $init = TRUE;
  33. }
  34. // yaml_parse() will error with an empty value.
  35. if (!trim($raw)) {
  36. return NULL;
  37. }
  38. // @todo Use ErrorExceptions when https://drupal.org/node/1247666 is in.
  39. // yaml_parse() will throw errors instead of raising an exception. Until
  40. // such time as Drupal supports native PHP ErrorExceptions as the error
  41. // handler, we need to temporarily set the error handler as ::errorHandler()
  42. // and then restore it after decoding has occurred. This allows us to turn
  43. // parsing errors into a throwable exception.
  44. // @see Drupal\Component\Serialization\Exception\InvalidDataTypeException
  45. // @see http://php.net/manual/class.errorexception.php
  46. set_error_handler([__CLASS__, 'errorHandler']);
  47. $ndocs = 0;
  48. $data = yaml_parse($raw, 0, $ndocs, [
  49. YAML_BOOL_TAG => '\Drupal\Component\Serialization\YamlPecl::applyBooleanCallbacks',
  50. ]);
  51. restore_error_handler();
  52. return $data;
  53. }
  54. /**
  55. * Handles errors for \Drupal\Component\Serialization\YamlPecl::decode().
  56. *
  57. * @param int $severity
  58. * The severity level of the error.
  59. * @param string $message
  60. * The error message to display.
  61. *
  62. * @see \Drupal\Component\Serialization\YamlPecl::decode()
  63. */
  64. public static function errorHandler($severity, $message) {
  65. restore_error_handler();
  66. throw new InvalidDataTypeException($message, $severity);
  67. }
  68. /**
  69. * {@inheritdoc}
  70. */
  71. public static function getFileExtension() {
  72. return 'yml';
  73. }
  74. /**
  75. * Applies callbacks after parsing to ignore 1.1 style booleans.
  76. *
  77. * @param mixed $value
  78. * Value from YAML file.
  79. * @param string $tag
  80. * Tag that triggered the callback.
  81. * @param int $flags
  82. * Scalar entity style flags.
  83. *
  84. * @return string|bool
  85. * FALSE, false, TRUE and true are returned as booleans, everything else is
  86. * returned as a string.
  87. */
  88. public static function applyBooleanCallbacks($value, $tag, $flags) {
  89. // YAML 1.1 spec dictates that 'Y', 'N', 'y' and 'n' are booleans. But, we
  90. // want the 1.2 behavior, so we only consider 'false', 'FALSE', 'true' and
  91. // 'TRUE' as booleans.
  92. if (!in_array(strtolower($value), ['false', 'true'], TRUE)) {
  93. return $value;
  94. }
  95. $map = [
  96. 'false' => FALSE,
  97. 'true' => TRUE,
  98. ];
  99. return $map[strtolower($value)];
  100. }
  101. }