ast
13 years ago
cache
13 years ago
XmlImportConfig.php
13 years ago
XmlImportCsvParse.php
13 years ago
XmlImportException.php
13 years ago
XmlImportParser.php
13 years ago
XmlImportReaderInterface.php
13 years ago
XmlImportStringReader.php
13 years ago
XmlImportTemplate.php
13 years ago
XmlImportTemplateCodeGenerator.php
13 years ago
XmlImportTemplateParser.php
13 years ago
XmlImportTemplateScanner.php
13 years ago
XmlImportToken.php
13 years ago
pclzip.lib.php
13 years ago
XmlImportTemplateCodeGenerator.php
383 lines
| 1 | <?php |
| 2 | /** |
| 3 | * @author Olexandr Zanichkovsky <olexandr.zanichkovsky@zophiatech.com> |
| 4 | * @package General |
| 5 | */ |
| 6 | |
| 7 | require_once dirname(__FILE__) . '/XmlImportConfig.php'; |
| 8 | require_once dirname(__FILE__) . '/ast/XmlImportAstSequence.php'; |
| 9 | require_once dirname(__FILE__) . '/ast/XmlImportAstText.php'; |
| 10 | require_once dirname(__FILE__) . '/ast/XmlImportAstPrint.php'; |
| 11 | require_once dirname(__FILE__) . '/ast/XmlImportAstInteger.php'; |
| 12 | require_once dirname(__FILE__) . '/ast/XmlImportAstFloat.php'; |
| 13 | require_once dirname(__FILE__) . '/ast/XmlImportAstString.php'; |
| 14 | require_once dirname(__FILE__) . '/ast/XmlImportAstXPath.php'; |
| 15 | require_once dirname(__FILE__) . '/ast/XmlImportAstWith.php'; |
| 16 | require_once dirname(__FILE__) . '/ast/XmlImportAstForeach.php'; |
| 17 | require_once dirname(__FILE__) . '/ast/XmlImportAstIf.php'; |
| 18 | require_once dirname(__FILE__) . '/ast/XmlImportAstMath.php'; |
| 19 | require_once dirname(__FILE__) . '/ast/XmlImportAstSpintax.php'; |
| 20 | |
| 21 | /** |
| 22 | * Is used to generate a PHP code from AST (Abstract Syntax Tree) |
| 23 | */ |
| 24 | class XmlImportTemplateCodeGenerator |
| 25 | { |
| 26 | |
| 27 | /** |
| 28 | * Top level statement sequence |
| 29 | * |
| 30 | * @var XmlImportAstSequence |
| 31 | */ |
| 32 | private $sequence; |
| 33 | |
| 34 | /** |
| 35 | * statement sequence stack |
| 36 | * |
| 37 | * @var array |
| 38 | */ |
| 39 | private $sequenceStack = array(); |
| 40 | |
| 41 | /** |
| 42 | * SimpleXmlElement variable number |
| 43 | * |
| 44 | * @var int |
| 45 | */ |
| 46 | private $xmlVariableNumber = 0; |
| 47 | |
| 48 | /** |
| 49 | * Previous statement |
| 50 | * |
| 51 | * @var XmlImportAstStatement |
| 52 | */ |
| 53 | private $previousStatement = null; |
| 54 | |
| 55 | /** |
| 56 | * Stack of SimpleXmlElement instances |
| 57 | * |
| 58 | * @var array |
| 59 | */ |
| 60 | private $xmlStack = array(); |
| 61 | |
| 62 | /** |
| 63 | * Whether PHP tag is open |
| 64 | * |
| 65 | * @var bool |
| 66 | */ |
| 67 | private $isPhpTagOpen = false; |
| 68 | |
| 69 | /** |
| 70 | * Creates new instance |
| 71 | * |
| 72 | * @param XmlImportAstSequence $sequence |
| 73 | */ |
| 74 | public function __construct(XmlImportAstSequence $sequence) |
| 75 | { |
| 76 | $this->sequence = $sequence; |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Generates the code and returns the filename under which it was stored in |
| 81 | * cache directory. If $filename is null then it is generated automatically |
| 82 | * |
| 83 | * @param string $filename |
| 84 | * @return string |
| 85 | */ |
| 86 | public function generate($filename = null) |
| 87 | { |
| 88 | $result = ''; |
| 89 | if (count($this->sequence->getVariables())) |
| 90 | { |
| 91 | $var = '$x' . $this->xmlVariableNumber++; |
| 92 | array_push($this->xmlStack, $var); |
| 93 | $result .= $this->openPhpTag() . $var . ' = $this->xml;' ; |
| 94 | } |
| 95 | $result .= $this->generateForSequence($this->sequence); |
| 96 | |
| 97 | if (count($this->sequence->getVariables())) |
| 98 | { |
| 99 | array_pop($this->xmlStack); |
| 100 | } |
| 101 | if (is_null($filename)) |
| 102 | { |
| 103 | $filename = tempnam(XmlImportConfig::getInstance()->getCacheDirectory(), 'xim'); |
| 104 | } |
| 105 | |
| 106 | file_put_contents($filename, $result); |
| 107 | return $filename; |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * Generates code for a statement sequence |
| 112 | * |
| 113 | * @param XmlImportAstSequence $sequence |
| 114 | * @return string |
| 115 | */ |
| 116 | private function generateForSequence(XmlImportAstSequence $sequence) |
| 117 | { |
| 118 | array_push($this->sequenceStack, $sequence); |
| 119 | $result = ''; |
| 120 | if (count($sequence->getVariableDefinitions()) > 0) |
| 121 | { |
| 122 | $result .= $this->openPhpTag(); |
| 123 | foreach ($sequence->getVariableDefinitions() as $xpath) |
| 124 | { |
| 125 | $result .= PHP_EOL . str_replace('{{XML}}', $this->xmlStack[count($this->xmlStack) - 1], $xpath); |
| 126 | } |
| 127 | $result .= PHP_EOL; |
| 128 | } |
| 129 | foreach ($sequence->getStatements() as $statement) |
| 130 | { |
| 131 | $result .= $this->generateForStatement($statement); |
| 132 | } |
| 133 | array_pop($this->sequenceStack); |
| 134 | |
| 135 | return $result; |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Generates code for a statement |
| 140 | * |
| 141 | * @param XmlImportAstStatement $statement |
| 142 | * @return string |
| 143 | */ |
| 144 | private function generateForStatement(XmlImportAstStatement $statement) |
| 145 | { |
| 146 | $result = ''; |
| 147 | if ($statement instanceof XmlImportAstText) |
| 148 | { |
| 149 | $result .= $this->closePhpTag(); |
| 150 | $text = preg_replace('%<\?|\?>%', '<?php echo "$0"; ?>', $statement->getValue()); // escape php tags |
| 151 | if ($this->previousStatement instanceof XmlImportAstPrint && (strpos($text, "\n") === 0 || strpos($text, "\r\n") === 0)) |
| 152 | { |
| 153 | $result .= PHP_EOL; |
| 154 | } |
| 155 | $result .= $text; |
| 156 | } |
| 157 | else |
| 158 | { |
| 159 | $result .= $this->openPhpTag(); |
| 160 | if ($statement instanceof XmlImportAstPrint) |
| 161 | { |
| 162 | $result .= 'echo '; |
| 163 | $result .= $this->generateForExpression($statement->getValue(), true) . ';'; |
| 164 | } |
| 165 | elseif ($statement instanceof XmlImportAstWith) |
| 166 | { |
| 167 | $var = '$x' . $this->xmlVariableNumber++; |
| 168 | $result .= PHP_EOL . $var . ' = ' ; |
| 169 | $result .= $this->generateForExpression($statement->getXpath()) . ';' . PHP_EOL; |
| 170 | |
| 171 | array_push($this->xmlStack, $var); |
| 172 | $result .= 'if (' . $var . ' !== false && count(' . $var . ') > 0) :' . PHP_EOL . $var . |
| 173 | ' = ' . $var . '[0];' . PHP_EOL; |
| 174 | $result .= $this->generateForSequence($statement->getBody()); |
| 175 | array_pop($this->xmlStack); |
| 176 | $result .= $this->openPhpTag() . PHP_EOL . 'endif;' . PHP_EOL; |
| 177 | } |
| 178 | elseif ($statement instanceof XmlImportAstForeach) |
| 179 | { |
| 180 | $var = '$x' . $this->xmlVariableNumber++; |
| 181 | $result .= PHP_EOL . 'foreach (' . $this->generateForExpression($statement->getXPath()) . |
| 182 | ' as ' . $var . ') :' . PHP_EOL; |
| 183 | array_push($this->xmlStack, $var); |
| 184 | $result .= $this->generateForSequence($statement->getBody()); |
| 185 | $result .= $this->openPhpTag() . PHP_EOL . 'endforeach;' . PHP_EOL; |
| 186 | array_pop($this->xmlStack); |
| 187 | } |
| 188 | elseif ($statement instanceof XmlImportAstIf) |
| 189 | { |
| 190 | $result .= PHP_EOL . 'if (' . $this->generateForExpression($statement->getCondition()) . ') :' . PHP_EOL; |
| 191 | $result .= $this->generateForSequence($statement->getIfBody()); |
| 192 | foreach ($statement->getElseIfs() as $elseif) |
| 193 | { |
| 194 | $result .= $this->openPhpTag() . PHP_EOL . 'elseif (' . $this->generateForExpression($elseif->getCondition()) . ') :' . PHP_EOL; |
| 195 | $result .= $this->generateForSequence($elseif->getBody()); |
| 196 | } |
| 197 | if (!is_null($body = $statement->getElseBody())) |
| 198 | { |
| 199 | $result .= $this->openPhpTag() . PHP_EOL . 'else :' . PHP_EOL; |
| 200 | $result .= $this->generateForSequence($body); |
| 201 | } |
| 202 | $result .= $this->openPhpTag() . PHP_EOL . 'endif;' . PHP_EOL; |
| 203 | } |
| 204 | |
| 205 | } |
| 206 | $this->previousStatement = $statement; |
| 207 | return $result; |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Generates code for expression |
| 212 | * |
| 213 | * @param XmlImportAstExpression $expression |
| 214 | * @param bool $inPrint whether in print or in clause or function argument |
| 215 | * @return string |
| 216 | */ |
| 217 | private function generateForExpression(XmlImportAstExpression $expression, $inPrint = false) |
| 218 | { |
| 219 | |
| 220 | switch (get_class($expression)) |
| 221 | { |
| 222 | case 'XmlImportAstString': |
| 223 | $result = '"' . $this->getEscapedValue($expression->getValue()) . '"'; |
| 224 | break; |
| 225 | |
| 226 | case 'XmlImportAstInteger': |
| 227 | case 'XmlImportAstFloat': |
| 228 | $result = $expression->getValue(); |
| 229 | break; |
| 230 | |
| 231 | case 'XmlImportAstXPath': |
| 232 | if ($inPrint) |
| 233 | { |
| 234 | $variables = $this->sequenceStack[count($this->sequenceStack) - 1]->getVariables(); |
| 235 | $result = '$this->getValue(' . $variables[$expression->getValue()] . ')'; |
| 236 | } |
| 237 | else |
| 238 | { |
| 239 | $variables = $this->sequenceStack[count($this->sequenceStack) - 1]->getVariables(); |
| 240 | $result = $variables[$expression->getValue()]; |
| 241 | } |
| 242 | break; |
| 243 | |
| 244 | case 'XmlImportAstMath': |
| 245 | $result = $this->generateForMath($expression); |
| 246 | break; |
| 247 | |
| 248 | case 'XmlImportAstSpintax': |
| 249 | $result = $this->generateForSpintax($expression); |
| 250 | break; |
| 251 | } |
| 252 | return $result; |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * Generates code for a function |
| 257 | * |
| 258 | * @param XmlImportAstFunction $function |
| 259 | * @return string |
| 260 | */ |
| 261 | private function generateForMath(XmlImportAstMath $math) |
| 262 | { |
| 263 | $result = ''; |
| 264 | $arguments = $math->getArguments(); |
| 265 | for($i = 0; $i < count($arguments); $i++) |
| 266 | { |
| 267 | $result .= $this->generateForExpression($arguments[$i], true); |
| 268 | } |
| 269 | |
| 270 | return 'number_format('.str_replace("\"", "", $result).',2)'; |
| 271 | } |
| 272 | |
| 273 | /** |
| 274 | * Generates code for a function |
| 275 | * |
| 276 | * @param XmlImportAstSpintax $expression |
| 277 | * @return string |
| 278 | */ |
| 279 | private function generateForSpintax(XmlImportAstSpintax $spintax) |
| 280 | { |
| 281 | $result = ''; |
| 282 | $arguments = $spintax->getArguments(); |
| 283 | $elements = array(); |
| 284 | $buf = array(); |
| 285 | |
| 286 | for($i = 0; $i < count($arguments); $i++) |
| 287 | { |
| 288 | |
| 289 | if ($arguments[$i]->getValue() != '|') array_push($buf, $this->generateForExpression($arguments[$i], true)); else { array_push($elements, $buf); $buf = array();} |
| 290 | |
| 291 | } |
| 292 | |
| 293 | array_push($elements, $buf); |
| 294 | |
| 295 | if (!empty($elements) and is_array($elements)){ |
| 296 | |
| 297 | $spintax_arr = $this->generateVariation($elements); |
| 298 | |
| 299 | foreach ($spintax_arr as $key => $value) { |
| 300 | $result .= "\"<p>\".".implode(".", $value).".\"</p>\""; if ($key != count($spintax_arr) - 1) $result .= "."; |
| 301 | } |
| 302 | |
| 303 | } |
| 304 | |
| 305 | return $result; |
| 306 | } |
| 307 | |
| 308 | function generateVariation($A, $i = 0) |
| 309 | { |
| 310 | $result = array(); |
| 311 | |
| 312 | if ($i < count($A)) |
| 313 | { |
| 314 | $variations = $this->generateVariation($A, $i + 1); |
| 315 | |
| 316 | for ($j = 0; $j < count($A[$i]); $j++) |
| 317 | { |
| 318 | if ($variations) |
| 319 | { |
| 320 | foreach ($variations as $variation) |
| 321 | { |
| 322 | $result[] = array_merge(array($A[$i][$j]), $variation); |
| 323 | } |
| 324 | } |
| 325 | else |
| 326 | { |
| 327 | $result[] = array($A[$i][$j]); |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | return $result; |
| 333 | } |
| 334 | /** |
| 335 | * Add PHP open tag if needed |
| 336 | * |
| 337 | * @return string |
| 338 | */ |
| 339 | private function openPhpTag() |
| 340 | { |
| 341 | $result = ''; |
| 342 | if (!$this->isPhpTagOpen) |
| 343 | { |
| 344 | $this->isPhpTagOpen = true; |
| 345 | $result = '<?php '; |
| 346 | } |
| 347 | return $result; |
| 348 | } |
| 349 | |
| 350 | /** |
| 351 | * Adds PHP close tag if needed |
| 352 | * |
| 353 | * @return string |
| 354 | */ |
| 355 | private function closePhpTag() |
| 356 | { |
| 357 | $result = ''; |
| 358 | if ($this->isPhpTagOpen) |
| 359 | { |
| 360 | $this->isPhpTagOpen = false; |
| 361 | $result = '?>'; |
| 362 | } |
| 363 | return $result; |
| 364 | } |
| 365 | |
| 366 | /** |
| 367 | * Gets escaped value |
| 368 | * |
| 369 | * @return string |
| 370 | */ |
| 371 | private function getEscapedValue($value) |
| 372 | { |
| 373 | $escapedValue = strtr($value, array( |
| 374 | "\n" => "\\n", |
| 375 | "\t" => "\\t", |
| 376 | "\r" => "\\r", |
| 377 | "$" => "\\$", |
| 378 | "\"" => "\\\"", |
| 379 | "\\" => "\\\\", |
| 380 | )); |
| 381 | return $escapedValue; |
| 382 | } |
| 383 | } |