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

/tine20/library/qCal/lib/qCal/Parser/Lexer/iCalendar.php

https://gitlab.com/rsilveira1987/Expresso
PHP | 137 lines | 73 code | 8 blank | 56 comment | 15 complexity | 8f54366047e105904bb1c6d2d5996992 MD5 | raw file
  1. <?php
  2. /**
  3. * qCal_Parser_Lexer_iCalendar
  4. * The lexer for iCalendar RFC 2445 format. Other formats will need their
  5. * own lexer. The lexer converts text to an array of "tokens", which, at least
  6. * for now, are just arrays.
  7. *
  8. * @package qCal
  9. * @subpackage qCal_Parser
  10. * @copyright Luke Visinoni (luke.visinoni@gmail.com)
  11. * @author Luke Visinoni (luke.visinoni@gmail.com)
  12. * @license GNU Lesser General Public License
  13. * @todo Make sure that multi-value properties are taken care of properly
  14. */
  15. class qCal_Parser_Lexer_iCalendar extends qCal_Parser_Lexer {
  16. /**
  17. * Constructor
  18. * @param string The iCalendar data to be parsed
  19. * @access public
  20. */
  21. public function __construct($content) {
  22. parent::__construct($content);
  23. }
  24. /**
  25. * Return a list of tokens (to be fed to the parser)
  26. * @return array tokens
  27. * @access public
  28. */
  29. public function tokenize() {
  30. // iCalendar data is "folded", which means that long lines are broken
  31. // into smaller lines and proceded by a space. The lines are "unfolded"
  32. // before being parsed.
  33. $lines = $this->unfold($this->content);
  34. // loop through chunks of input text by separating properties and components
  35. // and create tokens for each one, creating a multi-dimensional array of tokens to return
  36. // this "stack" array is used to keep track of the "current" component
  37. $stack = array();
  38. foreach ($lines as $line) {
  39. // each component starts with the string "BEGIN:" and doesn't end until the "END:" line
  40. if (preg_match('#^BEGIN:([a-z]+)$#i', $line, $matches)) {
  41. // create new array representing the new component
  42. $array = array(
  43. 'component' => $matches[1],
  44. 'properties' => array(),
  45. 'children' => array(),
  46. );
  47. // add the component to the stack
  48. $stack[] = $array;
  49. } elseif (strpos($line, "END:") === 0) {
  50. // end component, pop the stack
  51. $child = array_pop($stack);
  52. if (empty($stack)) {
  53. // if the stack is empty, assign the $child variable to the $tokens variable
  54. $tokens = $child;
  55. } else {
  56. // if the stack is not empty, create a reference of the last item in the stack
  57. $parent =& $stack[count($stack)-1];
  58. // add the child to the parent
  59. array_push($parent['children'], $child);
  60. }
  61. } else {
  62. // continue component
  63. if (preg_match('#^([^:]+):"?([^\n]+)?"?$#i', $line, $matches)) {
  64. // @todo What do I do with empty values?
  65. $value = isset($matches[2]) ? $matches[2] : "";
  66. // set the $component variable to a reference of the last item in the stack
  67. $component =& $stack[count($stack)-1];
  68. // if line is a property line, start a new property, but first determine if there are any params
  69. $property = $matches[1];
  70. $params = array();
  71. $propparts = explode(";", $matches[1]);
  72. if (count($propparts) > 1) {
  73. foreach ($propparts as $key => $part) {
  74. // the first one is the property name
  75. if ($key == 0) {
  76. $property = $part;
  77. } else {
  78. // the rest are params
  79. // @todo Quoted param values need to be taken care of...
  80. list($paramname, $paramvalue) = explode("=", $part, 2);
  81. $params[] = array(
  82. 'param' => $paramname,
  83. 'value' => $paramvalue,
  84. );
  85. }
  86. }
  87. }
  88. // set up the property array
  89. $proparray = array(
  90. 'property' => $property,
  91. 'value' => $value,
  92. 'params' => $params,
  93. );
  94. // assign the property array to the current component
  95. $component['properties'][] = $proparray;
  96. }
  97. }
  98. }
  99. // we should now have an associative, multi-dimensional array of tokens to return
  100. return $tokens;
  101. }
  102. /**
  103. * Long lines inside of an iCalendar file are "folded". This means that
  104. * they are broken into smaller lines and prepended with a space
  105. * character. This method "unfolds" these lines into one long line again.
  106. * @param string $content The iCalendar data to be unfolded
  107. * @return string The iCalendar data, unfolded
  108. * @access protected
  109. */
  110. protected function unfold($content) {
  111. $return = array();
  112. // handle linux AND windows line-breaks!
  113. $content = str_replace("\r", "\n", $content);
  114. $lines = explode("\n", $content);
  115. foreach ($lines as $line) {
  116. $checkempty = trim($line);
  117. if (empty($checkempty)) continue;
  118. $chr1 = substr($line, 0, 1);
  119. $therest = substr($line, 1);
  120. // if character 1 is a whitespace character... (tab or space)
  121. if ($chr1 == chr(9) || $chr1 == chr(32)) {
  122. $return[count($return)-1] .= $therest;
  123. } else {
  124. $return[] = $line;
  125. }
  126. }
  127. return $return;
  128. }
  129. }