Example #1
0
 function NearTAL($template, $page, $variables = null, $directories = null)
 {
     $returnValue = array('errorCode' => constant('NearTAL_Error_None'));
     if (is_null($variables) || !is_array($variables)) {
         $variables = array();
     }
     if (array_key_exists('CONTEXT', $variables)) {
         unset($variables['CONTEXT']);
     }
     if (is_null($directories) || !is_array($directories)) {
         $directories = array();
     }
     $elements = array('templates', 'cache', 'temp');
     foreach ($elements as $element) {
         if (!array_key_exists($element, $directories)) {
             $directories[$element] = $element;
         }
         if (DIRECTORY_SEPARATOR == $directories[$element][strlen($directories[$element]) - 1]) {
             $directories[$element] = substr($directories[$element], 0, -1);
         }
     }
     if (file_exists($directories['templates'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.html')) {
         if ((file_exists($directories['cache']) || mkdir($directories['cache'], 0700)) && is_dir($directories['cache']) && ((file_exists($directories['cache'] . DIRECTORY_SEPARATOR . $template) || mkdir($directories['cache'] . DIRECTORY_SEPARATOR . $template, 0700)) && is_dir($directories['cache'] . DIRECTORY_SEPARATOR . $template))) {
             if (file_exists($directories['cache'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.php')) {
                 $filesMTime = array('templateFile' => filemtime($directories['templates'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.html'), 'metalFile' => file_exists($tmpVariable = $directories['templates'] . DIRECTORY_SEPARATOR . $template . '.metal.html') ? filemtime($tmpVariable) : time(), 'nearTAL' => filemtime(__FILE__), 'nearTALCompiler' => filemtime('NearTAL.Compiler.php'));
                 $tmpVariable = filemtime($directories['cache'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.php');
                 $delete = false;
                 while (!$delete && ($fileMTime = array_pop($filesMTime))) {
                     if ($tmpVariable < $fileMTime) {
                         $delete = true;
                     }
                 }
                 if ($delete) {
                     unlink($directories['cache'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.php');
                 }
             }
             if (!file_exists($directories['cache'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.php')) {
                 require_once 'NearTAL.Compiler.php';
                 NearTAL_Compiler($returnValue, $template, $page, $variables, $directories);
             }
             if (file_exists($directories['cache'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.php')) {
                 include $directories['cache'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.php';
             } else {
                 NearTAL_AddError($returnValue, constant('NearTAL_Error_Cache'), $directories['cache'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.php');
             }
         } else {
             NearTAL_AddError($returnValue, constant('NearTAL_Error_Path'), null);
         }
     } else {
         NearTAL_AddError($returnValue, constant('NearTAL_Error_Template'), $directories['templates'] . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR . $page . '.html');
     }
     return $returnValue;
 }
 function NearTAL_Compiler(&$returnValue, $template, $page, $variables = null, $directories = null)
 {
     $compilerData = array('metal' => array(), 'keywords' => array('define', 'condition', 'repeat', 'replace', 'content', 'omit-tag', 'attributes'));
     if (file_exists($directories['templates']) && is_dir($directories['templates']) && file_exists($directories['templates'] . '/' . $template) && is_dir($directories['templates'] . '/' . $template) && (file_exists($directories['temp']) || mkdir($directories['temp'], 0700)) && is_dir($directories['temp'])) {
         if (file_exists($directories['templates'] . '/' . $template . '.metal.html') && false !== ($content = file_get_contents($directories['templates'] . '/' . $template . '.metal.html'))) {
             $parser = str_get_dom($content);
             NearTAL_Compiler_ParseMETAL($returnValue, $compilerData, $parser, false);
             unset($parser);
         }
         if (file_exists($directories['templates'] . '/' . $template . '/' . $page . '.html') && false !== ($content = file_get_contents($directories['templates'] . '/' . $template . '/' . $page . '.html'))) {
             $parser = str_get_dom($content);
             NearTAL_Compiler_ParseMETAL($returnValue, $compilerData, $parser, true);
             $nodes = $parser->select('[data-metal-use-macro]');
             foreach ($nodes as $node) {
                 $macroName = $node->getAttribute('data-metal-use-macro');
                 if (array_key_exists($macroName, $compilerData['metal'])) {
                     $availableSlots = array();
                     $slots = $node('[data-metal-fill-slot]');
                     foreach ($slots as $slot) {
                         $slotName = $slots->getAttribute('data-metal-fill-slot');
                         if (array_key_exists($slotName, $slots)) {
                             NearTAL_AddError($returnValue, constant('NearTAL_Error_DuplicatedSlot'), $slotName);
                         }
                         $slot->deleteAttribute('data-metal-fill-slot');
                         $availableSlots[$slotName] = $slot->getOuterText();
                     }
                     $node->setOuterText($compilerData['metal'][$macroName]);
                     $slots = $node('[data-metal-define-slot]');
                     foreach ($slots as $slot) {
                         $slotName = $slots->getAttribute('data-metal-define-slot');
                         if (array_key_exists($slotName, $availableSlots)) {
                             $slot->setOuterText($availableSlots[$slotName]);
                         } else {
                             $slot->deleteAttribute('data-metal-define-slot');
                             NearTAL_AddError($returnValue, constant('NearTAL_Error_UnknownSlot'), $slotName);
                         }
                     }
                     unset($availableSlots);
                 } else {
                     NearTAL_AddError($returnValue, constant('NearTAL_Error_UnknownMacro'), $macroName);
                 }
             }
             $output = '<?php if(defined(\'NearTAL\')){$localVariables=array(\'defined\'=>array(),\'repeat\'=>array(),\'stack\'=>array(\'defined\'=>array(),\'repeat\'=>array()),\'template\'=>array()); ?>';
             $localVariablesIndex = 0;
             $count = 0;
             $selectFilter = array();
             foreach ($compilerData['keywords'] as $keyword) {
                 $selectFilter[] = '[data-tal-' . $keyword . ']';
             }
             $nodes = $parser->select(implode(',', $selectFilter));
             $localVariablesIndex = 0;
             foreach ($nodes as $node) {
                 $changes = array('attributes' => array(), 'outside' => array('pre' => null, 'post' => null), 'inside' => array('pre' => null, 'post' => null));
                 foreach ($compilerData['keywords'] as $keyword) {
                     $attribute = trim($node->getAttribute('data-tal-' . $keyword));
                     if (!is_null($attribute)) {
                         if (!is_array($attribute) && !empty($attribute)) {
                             switch ($keyword) {
                                 case 'define':
                                     $tmpVariable = array();
                                     $tmpVariable['count'] = preg_match_all('/[\\s]*(?:(?:(local|world)[\\s]+)?(.+?)[\\s]+(.+?)[\\s]*(?:(?<!;);(?!;)|$))+?/', $attribute, $tmpVariable['elements'], PREG_SET_ORDER);
                                     for ($j = 0; $j < $tmpVariable['count']; $j++) {
                                         $tmpVariable['name'] = str_replace('\'', '&#039', $tmpVariable['elements'][$j][2]);
                                         $fastAnswers = NearTAL_Compiler_ParseTALES($tmpVariable['compiledExpression'], $tmpVariable['elements'][$j][3], $node->attributes, '$tmpVariable');
                                         $changes['outside']['pre'] .= '$localVariables[\'template\'][' . $localVariablesIndex . ']=(' . $tmpVariable['compiledExpression'] . '&&!$tmpVariable[1]);if($localVariables[\'template\'][' . $localVariablesIndex++ . ']){NearTAL_LocalVariablesPush($localVariables,\'defined\',\'' . $tmpVariable['name'] . '\',$tmpVariable[0]);}';
                                         if ('world' != $tmpVariable['elements'][$j][1]) {
                                             $changes['outside']['post'] = 'if($localVariables[\'template\'][' . ($localVariablesIndex - 1) . ']){NearTAL_LocalVariablesPop($localVariables,\'defined\',\'' . $tmpVariable['name'] . '\');unset($localVariables[\'template\'][' . ($localVariablesIndex - 1) . ']);}' . $changes['outside']['post'];
                                         }
                                     }
                                     break;
                                 case 'condition':
                                     $fastAnswers = NearTAL_Compiler_ParseTALES($tmpVariable['compiledExpression'], $attribute, $node->attributes, '$localVariables[\'template\'][' . $localVariablesIndex . ']', true, false);
                                     if ($fastAnswers[1]) {
                                         $node->delete();
                                     } else {
                                         $changes['outside']['pre'] .= 'if(' . $tmpVariable['compiledExpression'] . '){';
                                         $changes['outside']['post'] = '}unset($localVariables[\'template\'][' . $localVariablesIndex++ . ']);' . $changes['outside']['post'];
                                     }
                                     break;
                                 case 'repeat':
                                     $tmpVariable['elements'] = explode(" ", $attribute, 2);
                                     $tmpVariable['name'] = str_replace('\'', '&#039', $tmpVariable['elements'][0]);
                                     $fastAnswers = NearTAL_Compiler_ParseTALES($tmpVariable['compiledExpression'], $tmpVariable['elements'][1], $node->attributes, '$localVariables[\'template\'][' . $localVariablesIndex . ']');
                                     $changes['outside']['pre'] .= sprintf('if((%1$s)&&($localVariables[\'template\'][%2$d][1]||is_array($localVariables[\'template\'][%2$d][0]))){$localVariables[\'template\'][%3$d]=array(false,false);if(!$localVariables[\'template\'][%2$d][1]){($localVariables[\'template\'][%3$d][0]=true)&&NearTAL_LocalVariablesPush($localVariables,\'repeat\',\'%4$s\',null);($localVariables[\'template\'][%3$d][1]=true)&&NearTAL_LocalVariablesPush($localVariables,\'defined\',\'%4$s\',null);$localVariables[\'repeat\'][\'%4$s\'][\'index\']=-1;$localVariables[\'repeat\'][\'%4$s\'][\'length\']=count($localVariables[\'template\'][%2$d][0]);}do{if(!$localVariables[\'template\'][%2$d][1]){$localVariables[\'defined\'][\'%4$s\']=array_shift($localVariables[\'template\'][%2$d][0]);$localVariables[\'repeat\'][\'%4$s\'][\'index\']++;$localVariables[\'repeat\'][\'%4$s\'][\'number\']=$localVariables[\'repeat\'][\'%4$s\'][\'index\']+1;$localVariables[\'repeat\'][\'%4$s\'][\'even\']=($localVariables[\'repeat\'][\'%4$s\'][\'number\']%%2?true:false);$localVariables[\'repeat\'][\'%4$s\'][\'odd\']=!$localVariables[\'repeat\'][\'%4$s\'][\'even\'];$localVariables[\'repeat\'][\'%4$s\'][\'start\']=($localVariables[\'repeat\'][\'%4$s\'][\'index\']?false:true);$localVariables[\'repeat\'][\'%4$s\'][\'end\']=(($localVariables[\'repeat\'][\'%4$s\'][\'number\']==$localVariables[\'repeat\'][\'%4$s\'][\'length\'])?true:false);$localVariables[\'repeat\'][\'%4$s\'][\'letter\']=NearTAL_NumberToLetter($localVariables[\'repeat\'][\'%4$s\'][\'index\']);$localVariables[\'repeat\'][\'%4$s\'][\'Letter\']=strtoupper($localVariables[\'repeat\'][\'%4$s\'][\'letter\']);}', $tmpVariable['compiledExpression'], $localVariablesIndex++, $localVariablesIndex, $tmpVariable['name']);
                                     $changes['outside']['post'] = sprintf('}while(!$localVariables[\'template\'][%1$d][1]&&!empty($localVariables[\'template\'][%1$d][0]));$localVariables[\'template\'][%2$d][0]&&NearTAL_LocalVariablesPop($localVariables,\'repeat\',\'%3$s\');$localVariables[\'template\'][%2$d][1]&&NearTAL_LocalVariablesPop($localVariables,\'defined\',\'%3$s\');unset($localVariables[\'template\'][%1$d],$localVariables[\'template\'][%2$d]);}', $localVariablesIndex - 1, $localVariablesIndex++, $tmpVariable['name']) . $changes['outside']['post'];
                                     break;
                                 case 'replace':
                                 case 'content':
                                     if ('content' == $keyword || 'replace' == $keyword && !array_key_exists('data-tal-content', $node->attributes)) {
                                         $tmpVariable = array();
                                         if ('structure ' == substr($attribute, 0, 10) || 'text ' == substr($attribute, 0, 5)) {
                                             $tmpVariable['parameters'] = explode(" ", $attribute, 2);
                                         } else {
                                             $tmpVariable['parameters'] = array("text", $attribute);
                                         }
                                         $fastAnswers = NearTAL_Compiler_ParseTALES($tmpVariable['compiledExpression'], $tmpVariable['parameters'][1], $node->attributes, '$localVariables[\'template\'][' . $localVariablesIndex . ']');
                                         if (!$fastAnswers[0]) {
                                             if (!$fastAnswers[1]) {
                                                 $tmpVariable['pre'] = 'if(' . $tmpVariable['compiledExpression'] . '&&!$localVariables[\'template\'][' . $localVariablesIndex . '][1]&&!is_null($localVariables[\'template\'][' . $localVariablesIndex . '][0])){echo(' . ('text' == $tmpVariable['parameters'][0] ? 'str_replace(array(\'&\',\'<\',\'>\'),array(\'&amp\',\'&lt\',\'&gt\'),' : null) . '(is_bool($localVariables[\'template\'][' . $localVariablesIndex . '][0])?($localVariables[\'template\'][' . $localVariablesIndex . '][0]?1:0):$localVariables[\'template\'][' . $localVariablesIndex . '][0])' . ('text' == $tmpVariable['parameters'][0] ? ')' : null) . ');}elseif($localVariables[\'template\'][' . $localVariablesIndex . '][1]){';
                                                 $tmpVariable['post'] = '}unset($localVariables[\'template\'][' . $localVariablesIndex++ . ']);';
                                                 if ('content' == $keyword) {
                                                     $changes['inside']['pre'] = $tmpVariable['pre'] . $changes['inside']['pre'];
                                                     $changes['inside']['post'] .= $tmpVariable['post'];
                                                 } else {
                                                     $changes['outside']['pre'] .= $tmpVariable['pre'];
                                                     $changes['outside']['post'] = $tmpVariable['post'] . $changes['outside']['post'];
                                                 }
                                             } else {
                                                 if ('content' == $keyword) {
                                                     foreach ($node->children as $id => $child) {
                                                         $node->deleteChild($id);
                                                     }
                                                 } else {
                                                     $node->delete();
                                                 }
                                             }
                                         }
                                     }
                                     break;
                                 case 'attributes':
                                     $tmpVariable = array();
                                     $tmpVariable['count'] = preg_match_all('/(?:[\\s]*(.+?)[\\s]+(.+?)[\\s]*(?:(?<!;);(?!;)|$))+?/', $attribute, $tmpVariable['elements'], PREG_SET_ORDER);
                                     for ($j = 0; $j < $tmpVariable['count']; $j++) {
                                         $changes['attributes'][$tmpVariable['elements'][$j][1]] = $tmpVariable['elements'][$j][2];
                                     }
                                     break;
                                 case 'raw-attributes':
                                     $tmpVariable = array();
                                     $tmpVariable['count'] = preg_match_all('/(?:[\\s]*(.+?)[\\s]*$)/', $attribute, $tmpVariable['elements'], PREG_SET_ORDER);
                                     if (1 == $tmpVariable['count']) {
                                         $changes['raw-attributes'] = $tmpVariable['elements'][$j][1];
                                     }
                                     break;
                                 case 'omit-tag':
                                     $fastAnswers = NearTAL_Compiler_ParseTALES($tmpVariable['compiledExpression'], $attribute, $node->attributes, '$tmpVariable', true);
                                     if (!$fastAnswers[0] && !$fastAnswers[1]) {
                                         $changes['outside']['pre'] .= '$localVariables[\'template\'][' . $localVariablesIndex . ']=!(' . $tmpVariable['compiledExpression'] . ');if($localVariables[\'template\'][' . $localVariablesIndex . ']){';
                                         // If isn't selfclosed OR if I already need to put some code between starting and closing tag, I put some data "inside"
                                         // TODO: a regexp to remove ? >< ?php and {}
                                         if (!$node->self_close || isset($changes['inside']['pre']) || isset($changes['inside']['post']) || 0 < count($changes['attributes'])) {
                                             $changes['inside']['pre'] = '}' . $changes['inside']['pre'];
                                             $changes['inside']['post'] .= 'if($localVariables[\'template\'][' . $localVariablesIndex . ']){';
                                         }
                                         $changes['outside']['post'] = '}unset($localVariables[\'template\'][' . $localVariablesIndex++ . ']);' . $changes['outside']['post'];
                                     }
                                     break;
                             }
                         }
                         $node->deleteAttribute('data-tal-' . $keyword);
                     }
                 }
                 if (!empty($changes['attributes'])) {
                     foreach ($changes['attributes'] as $name => $value) {
                         $fastAnswers = NearTAL_Compiler_ParseTALES($tmpVariable['compiledExpression'], $value, $node->attributes, '$tmpVariable');
                         if (!$fastAnswers[0]) {
                             if (!$fastAnswers[1]) {
                                 $tmpVariable['exists'] = array_key_exists($name, $node->attributes);
                                 $tmpVariable['attribute'] = '<?php if(' . $tmpVariable['compiledExpression'] . (!$tmpVariable['exists'] ? '&&!$tmpVariable[1]' : null) . '){?>' . $name . '="<?php echo(' . ($tmpVariable['exists'] ? '$tmpVariable[1]?\'' . str_replace('\'', '&#039', $node->attributes[$name]) . '\':' : null) . '(is_bool($tmpVariable[0])?($tmpVariable[0]?1:0):$tmpVariable[0]));?>"<?php }?>';
                                 $node->attributes[$tmpVariable['attribute']] = $tmpVariable['attribute'];
                                 if ($tmpVariable['exists']) {
                                     unset($node->attributes[$name]);
                                 }
                             } else {
                                 unset($node->attributes[$name]);
                             }
                         }
                     }
                 }
                 if (!empty($changes['raw-attributes'])) {
                     $fastAnswers = NearTAL_Compiler_ParseTALES($tmpVariable['compiledExpression'], $changes['raw-attributes'], $node->attributes, '$tmpVariable');
                     if (!$fastAnswers[0]) {
                         if (!$fastAnswers[1]) {
                             $tmpVariable['attribute'] = '<?php if(' . $tmpVariable['compiledExpression'] . '&&!$tmpVariable[1]' . '){?> <?phpecho((is_bool($tmpVariable[0])?($tmpVariable[0]?1:0):$tmpVariable[0]));}?>';
                             $node->attributes[$tmpVariable['attribute']] = null;
                         }
                     }
                 }
                 if (!is_null($changes['outside']['pre'])) {
                     $position = $node->index(true);
                     $node->parent->addXML('php', ' ' . $changes['outside']['pre'], array(), $position);
                 }
                 if (!is_null($changes['outside']['post'])) {
                     $position = $node->index(true) + 1;
                     $node->parent->addXML('php', ' ' . $changes['outside']['post'], array(), $position);
                 }
                 if (!is_null($changes['inside']['pre'])) {
                     $position = 0;
                     $node->addXML('php', ' ' . $changes['inside']['pre'], array(), $position);
                 }
                 if (!is_null($changes['inside']['post'])) {
                     $position = -1;
                     $node->addXML('php', ' ' . $changes['inside']['post'], array(), $position);
                 }
             }
             $output .= str_replace("?>\n", "?>\n\n", str_replace("\r", '', $parser)) . '<?php }?>';
             unset($parser);
             $file = null;
             if (false !== ($temporaryFilename = tempnam($directories['temp'], 'TAL')) && false !== ($file = fopen($temporaryFilename, 'wb'))) {
                 fwrite($file, $output);
                 fclose($file);
                 if ((file_exists($directories['cache']) || mkdir($directories['cache'], 0700)) && is_dir($directories['cache']) && (file_exists($directories['cache'] . '/' . $template) || mkdir($directories['cache'] . '/' . $template, 0700)) && is_dir($directories['cache'] . '/' . $template)) {
                     if (!rename($temporaryFilename, $directories['cache'] . '/' . $template . '/' . $page . '.php')) {
                         NearTAL_AddError($returnValue, constant('NearTAL_Error_Rename'), $temporaryFilename);
                     }
                 } else {
                     NearTAL_AddError($returnValue, constant('NearTAL_Error_Cache'), null);
                 }
             } else {
                 NearTAL_AddError($returnValue, constant('NearTAL_Error_TemporaryFile'), null);
             }
         } else {
             NearTAL_AddError($returnValue, constant('NearTAL_Error_Template'), null);
         }
     } else {
         NearTAL_AddError($returnValue, constant('NearTAL_Error_Path'), null);
     }
 }