Initial commit

This commit is contained in:
2020-11-02 15:46:52 +01:00
commit 17f974127c
13788 changed files with 1921656 additions and 0 deletions

View File

@@ -0,0 +1,611 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) 2005-2013 OpenStudio */
/* email : info@thelia.fr */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program.  If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
require_once __DIR__ . "/../../fonctions/autoload.php";
class Analyse{
public $contenu;
public $tokens;
public $pile_nom_boucle;
public $pile_boucle_courante;
private $in_comment = false;
private static $no_debug;
public static $debug_text;
function __construct($allow_debug)
{
self::$no_debug = ! ($allow_debug && (DEBUG_PARSER || DEBUG_EVAL));
$this->tokens = array();
$this->pile_nom_boucles = array();
$this->pile_boucle_courante = array();
self::$debug_text = false;
}
function terminer()
{
if (! self::$no_debug)
{
self::$debug_text = '
<div style="border: 1px solid black; margin: 5px; background-color: white; color: black; text-align: left; font-size: 11px;">
<div style="border-bottom: 1px solid black; margin: 0; padding: 5px; background-color: #f0f0f0; font-weight: bold;">Information de debug du parser</div>
<pre style="margin: 0; padding: 5px; height: 200px; overflow: scroll;">'
. self::$debug_text
.'</pre></div>'
;
}
}
static function strlen_cmp($a, $b)
{
$la = strlen($a);
$lb = strlen($b);
if ($la == $lb) return 0;
return ($la > $lb) ? -1 : 1;
}
public static function echo_debug()
{
if (self::$no_debug) return;
$text = '';
$numargs = func_num_args();
for($idx = 0; $idx < $numargs; $idx++)
{
$arg = func_get_arg($idx);
$text .= is_scalar($arg) ? $arg : print_r($arg, true);
}
self::$debug_text .= '<pre>[DEBUG] ' . htmlspecialchars($text)."</pre>\n";
}
function parse_string(&$filecontents)
{
$this->tokens = preg_split("/( |\t|\n|\r|<|>|\\#|\")/", $filecontents, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
// Comme on fait un next() dans parse content, insérer une valeur non significative en début de tableau
array_unshift($this->tokens, '');
// if (DEBUG_PARSER) {Analyse::echo_debug("Tokens: ", $this->tokens); }
return $this->parse_content();
}
function parse_string_with_cache(&$filecontents, $cache_dir)
{
if (! is_dir($cache_dir))
{
if (mkdir($cache_dir, 0777, true) === false)
{
die('Impossible de créer le répertoire '.$cache_dir.'. Vérifiez les droits d\'accès');
}
}
$this->cleanup_cache($cache_dir);
$cache_file = $cache_dir . hash('md5', $filecontents) . '.cache';
if (file_exists($cache_file))
{
// Mettre à jour la date du fichier: les fichiers les plus souvent accédés restent plus longtemps dans le cache.
@touch($cache_file);
return unserialize(file_get_contents($cache_file));
}
else
{
$data = $this->parse_string($filecontents);
file_put_contents($cache_file, serialize($data));
return $data;
}
}
public static function cleanup_cache($cache_dir, $force = 0)
{
// Doit-on purger le cache ?
$last_check = intval(Variable::lire(Parseur::PREFIXE.'_cache_check_time'));
$check_period = intval(3600 * Variable::lire(Parseur::PREFIXE.'_cache_check_period'));
if ($force == 0 && time() - $last_check < $check_period) return;
Variable::ecrire(Parseur::PREFIXE.'_cache_check_time', time());
$cache_file_lifetime = 3600 * Variable::lire(Parseur::PREFIXE.'_cache_file_lifetime');
if ($dh = @opendir($cache_dir))
{
while ($file = readdir($dh))
{
if (strstr($file, '.cache') !== false)
{
$path = $cache_dir . $file;
$filemtime = @filemtime($path);
if (! $filemtime || (time() - $filemtime) >= $cache_file_lifetime )
{
@unlink($path);
}
}
}
@closedir($dh);
}
}
function parse_args()
{
$args = '';
$in_quote = false;
while (1)
{
$tok = next($this->tokens);
//if (DEBUG_PARSER) { Analyse::echo_debug("Parse args: tok='$tok', in_quote=$in_quote"); }
if ($tok == '#')
{
$token = next($this->tokens);
if (preg_match('/([A-Z0-9_]+)/', $token, $varname) > 0)
{
$tok = '#'.$varname[1];
//if (DEBUG_PARSER) { Analyse::echo_debug("new arg var: '$tok'"); }
}
$this->add_var($tok);
// Il faut placer dans les args la valeur originale (sinon bug. Ex: #PROMO[X][Y])
$tok = '#'.$token;
}
else if ($tok == '"')
{
$in_quote = ! $in_quote;
}
else if ($tok == '>')
{
if (! $in_quote) break;
}
else if ($tok === FALSE)
{
break;
}
$args .= $tok;
}
return $args;
}
function add_var($token)
{
// Ne pas prendre en compte les filtres et les get/set
if (strstr($token, 'FILTRE_') !== FALSE
||
strstr($token, 'GET{') !== FALSE
||
strstr($token, 'SET{') !== FALSE) return;
//if (DEBUG_PARSER) { Analyse::echo_debug("Variable: $token");}
$count = count($this->pile_boucle_courante);
// Pas de boucle ouverte
if ($count == 0) return;
//$boucle_courante = end($this->pile_boucle_courante);
$boucle_courante = $this->pile_boucle_courante[$count-1];
//if (DEBUG_PARSER) { Analyse::echo_debug("boucle courante: ", $boucle_courante); }
// La boucle n'est pas une boucle simple -> on ne stocke pas les variables
if ($boucle_courante->type() != PexToken::TYPE_BOUCLE_SIMPLE) return;
if (preg_match('/([A-Z0-9_]+)/', $token, $varname) > 0)
{
if (! in_array($varname[1], $boucle_courante->variables))
{
$boucle_courante->variables[] = $varname[1];
//if (DEBUG_PARSER) { Analyse::echo_debug("Variables de $boucle_courante->nom", $boucle_courante->variables);}
}
}
}
function controle_fermeture_boucle($nom_boucle)
{
$nom_pile = array_pop($this->pile_nom_boucles);
//if (DEBUG_PARSER) Analyse::echo_debug("Pile boucles: required: '$nom_boucle', popped: '/$nom_pile', ", $this->pile_nom_boucles);
if ($nom_boucle != '/' . $nom_pile)
{
if ($nom_pile == '')
{
die("Erreur de syntaxe: $nom_boucle: balise de fin sans balise de début.");
}
else
{
die("Erreur de syntaxe: $nom_boucle trouvé, /$nom_pile attendu.");
}
}
}
function process_token(&$atoken)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("enter process_token ", $atoken);}
$token_type = PexToken::TXT;
if ($atoken == '<')
{
$no_match = false;
$token = next($this->tokens);
if (DEBUG_PARSER) { Analyse::echo_debug("Next PexToken:[$token]");}
// Optimisation (gain: ~= 0,1 sec. sur index standard)
if ($token[0] != 'T' && strpos($token, '/T') !== 0 && strpos($token, '//T') !== 0 && $token[0] != 'R' && strpos($token, '/R') !== 0)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("N'est pas une boucle thelia");}
$no_match = true;
}
else
{
// Get token type
if (strpos($token, 'THELIA_') === 0)
$token_type = PexToken::OBS;
else if (strpos($token, '/THELIA_') === 0)
$token_type = PexToken::FBS;
else if (strpos($token, 'TEST_') === 0)
$token_type = PexToken::OBT;
else if (strpos($token, '/TEST_') === 0)
$token_type = PexToken::EBT;
else if (strpos($token, '//TEST_') === 0)
$token_type = PexToken::FBT;
else if (strpos($token, 'T_') === 0)
$token_type = PexToken::OBC;
else if (strpos($token, '/T_') === 0)
$token_type = PexToken::EBC;
else if (strpos($token, '//T_') === 0)
$token_type = PexToken::FBC;
else if (strpos($token, 'REM') === 0)
$token_type = PexToken::OCM;
else if (strpos($token, '/REM') === 0)
$token_type = PexToken::FCM;
else if (strpos($token, 'REPETER') === 0)
$token_type = PexToken::OBR;
else if (strpos($token, '/REPETER') === 0)
$token_type = PexToken::FBR;
else if (strpos($token, 'T:') === 0)
$token_type = PexToken::OBCV;
else if (strpos($token, '/T:') === 0)
$token_type = PexToken::EBCV;
else if (strpos($token, '//T:') === 0)
$token_type = PexToken::FBCV;
else
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Token type texte");}
$no_match = true;
}
}
if ($no_match)
{
prev($this->tokens);
$token = $atoken;
}
}
// Variables
else if ($atoken == '#')
{
// Traiter les cas similaires à ##REF
$tmp = next($this->tokens);
if ($tmp == '#')
{
$token = '#';
prev($this->tokens);
}
else
{
$token = '#' . $tmp;
$this->add_var($token);
}
}
else
{
$token = $atoken;
}
//if (DEBUG_PARSER) { Analyse::echo_debug( "Token:[$token], type $token_type"); }
// Dans un commentaire, on attend la fin sans rien analyser
if ($this->in_comment && $token_type !== PexToken::FCM)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("ignore: $token_type:", $token); }
return 'vide';
}
// BOUCLE SIMPLE et boucle REPETER
if ($token_type === PexToken::OBS || $token_type === PexToken::OBR)
{
if ($token_type === PexToken::OBS)
$boucle = new BoucleSimple(substr($token, 7));
else
$boucle = new BoucleRepeter(substr($token, 8));
array_push($this->pile_nom_boucles, $token);
// Parse args se fait avant le push, car les variables dans les args doivent être valuées
// par la boucle enclosante.
$boucle->set_args($this->parse_args());
array_push($this->pile_boucle_courante, $boucle);
//if (DEBUG_PARSER) { Analyse::echo_debug("Push boucle courante $boucle->nom\n", $this->pile_boucle_courante); }
$boucle->ajouter($this->parse_content());
// Skip remaining > TODO check >
$this->skipto('>');
return $boucle;
}
else if ($token_type === PexToken::FBS || $token_type === PexToken::FBR)
{
$this->controle_fermeture_boucle($token);
array_pop($this->pile_boucle_courante);
//if (DEBUG_PARSER) { Analyse::echo_debug("Pop boucle courante $token\n", $this->pile_boucle_courante); }
return 'stop';
}
// BOUCLE CONDITIONNELLE
else if ($token_type === PexToken::OBC)
{
$boucle = new BoucleConditionnelle(substr($token, 2));
$this->skipto('>');
array_push($this->pile_nom_boucles, $token);
//if (DEBUG_PARSER) { Analyse::echo_debug("Push boucle conditionnelle $token\n", $this->pile_boucle_courante); }
// Si
$boucle->ajouter($this->parse_content());
$this->skipto('>');
array_push($this->pile_nom_boucles, '/'.$token);
//if ( const ) { Analyse::echo_debug("Push SI boucle conditionnelle $token\n", $this->pile_boucle_courante); }
// Sinon
$boucle->ajouter($this->parse_content());
$this->skipto('>');
return $boucle;
}
else if ($token_type === PexToken::EBC)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Controle fermeture SI: $token\n", $this->pile_boucle_courante); }
$this->controle_fermeture_boucle($token);
return 'stop';
}
else if ($token_type === PexToken::FBC)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Controle fermeture ELSE: $token\n", $this->pile_boucle_courante);}
$this->controle_fermeture_boucle($token);
return 'stop';
}
// BOUCLE CONDITIONNELLE sur vaeriable
else if ($token_type === PexToken::OBCV)
{
$var = substr($token, 2);
// Ajouter la variable à la boucle enclosante
$this->add_var($var);
$boucle = new BoucleConditionnelleVariable($var);
$this->skipto('>');
array_push($this->pile_nom_boucles, $token);
//if (DEBUG_PARSER) { Analyse::echo_debug("Push boucle conditionnelle $token\n", $this->pile_boucle_courante); }
// Si
$boucle->ajouter($this->parse_content());
$this->skipto('>');
array_push($this->pile_nom_boucles, '/'.$token);
//if ( const ) { Analyse::echo_debug("Push SI boucle conditionnelle $token\n", $this->pile_boucle_courante); }
// Sinon
$boucle->ajouter($this->parse_content());
$this->skipto('>');
return $boucle;
}
else if ($token_type === PexToken::EBCV)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Controle fermeture SI: $token\n", $this->pile_boucle_courante); }
$this->controle_fermeture_boucle($token);
return 'stop';
}
else if ($token_type === PexToken::FBCV)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Controle fermeture ELSE: $token\n", $this->pile_boucle_courante);}
$this->controle_fermeture_boucle($token);
return 'stop';
}
// Boucle <TEST_xxx>
else if ($token_type === PexToken::OBT)
{
$boucle = new BoucleTest(substr($token, 5));
// Parse args se fait avant le push, car les variables dans les args doivent être valuées
// par la boucle enclosante.
$boucle->set_args($this->parse_args());
array_push($this->pile_nom_boucles, $token);
//if (DEBUG_PARSER) { Analyse::echo_debug("Push boucle Test $boucle->nom\n", $this->pile_boucle_courante);}
// Si
$boucle->ajouter($this->parse_content());
$this->skipto('>');
array_push($this->pile_nom_boucles, '/' . $token);
// Sinon
$boucle->ajouter($this->parse_content());
$this->skipto('>');
return $boucle;
}
else if ($token_type === PexToken::EBT)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Controle fermeture TEST SI: $token\n", $this->pile_boucle_courante);}
$this->controle_fermeture_boucle($token);
return 'stop';
}
else if ($token_type === PexToken::FBT)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Controle fermeture TEST ELSE: $token\n", $this->pile_boucle_courante); }
$this->controle_fermeture_boucle($token);
return 'stop';
}
else if ($token_type === PexToken::OCM)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Ouverture commentaire: $token\n", $this->pile_boucle_courante); }
$this->in_comment = true;
return 'vide';
}
else if ($token_type === PexToken::FCM)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("Controle fermeture TEST ELSE: $token\n", $this->pile_boucle_courante); }
$this->in_comment = false;
$this->skipto('>');
return 'vide';
}
else if ($token !== '')
{
return new PexTexte($token);
}
else
{
return 'vide';
}
}
function skipto($val)
{
$ret = false;
while ( ( ($tok = next($this->tokens)) !== false) && $tok != $val )
{
//if (DEBUG_PARSER) { Analyse::echo_debug("skipping $tok"); }
$ret = $tok;
}
}
function parse_content()
{
$contenu = new ContenuElement();
$i = false;
while (true)
{
$token = next($this->tokens);
// Done !
if ($token === FALSE)
{
//if (DEBUG_PARSER) { Analyse::echo_debug("No more tokens.");}
// Si on est encore dans un commentaire, failed !
if ($this->in_comment)
{
die('Erreur de syntaxe: un commentaire n\'a pas été fermé.');
}
// S'il reste des choses dans la pile des noms, des boucles n'ont pas été fermées correctement.
if (count($this->pile_nom_boucles) > 0)
{
die('Erreur de syntaxe: une ou plusieurs boucles n\'ont pas été fermées: '.implode(', ', $this->pile_nom_boucles));
}
break;
}
$res = $this->process_token($token);
if ($res == 'vide')
continue;
else if ($res == 'stop')
break;
else
$contenu->ajouter($res);
}
return $contenu;
}
}
?>