@ 2022-02-27T19:12:59Z <?php
const TK_WHITESPACES = 0,
TK_OPENING_PARENTHESIS = 2, // 0b00000010
TK_CLOSING_PARENTHESIS = 4, // 0b00000100
TK_LOGICAL_CONJUNCTION = 8, // 0b00001000
TK_LOGICAL_DISJUNCTION = 16, // 0b00010000
TK_LOGICAL_NEGATION = 32, // etc.
TK_STRING = 64,
TK_EQUAL = 128,
TK_NOT_EQUAL = 256,
TK_COLUMN_NAME = 512,
TK_END = 1024;
const ERR_NONE = 0,
ERROR = 1; // 0b00000001
const TOKENIZATION_ERROR = 3, // 0b00000011
ERR_UNEXPECTED_CHAR = 7, // 0b00000111
ERR_UNCLOSED_STRING = 11, // 0b00001011
ERR_BAD_UTF8 = 19, // 0b00010011
ERR_EMPTY_STRING = 35; // TE
const LOGIC_ERROR = 129, // 0b10000001
ERR_UNEXPECTED_TOKEN = 133, // 0b10000101
ERR_UNEXPECTED_CLOSING_PARENTHESIS = 137, // 0b10001001
ERR_MISSING_CLOSING_PARENTHESIS = 145, // 0b10010001
ERR_UNKNOW_COLUMN_NAME = 161, // 0b10100001
ERR_EMPTY_QUERY = 193; // L E
const RULES = [
TK_COLUMN_NAME => TK_EQUAL | TK_NOT_EQUAL,
TK_EQUAL => TK_STRING,
TK_NOT_EQUAL => TK_STRING,
TK_STRING => TK_END | TK_CLOSING_PARENTHESIS | TK_LOGICAL_DISJUNCTION | TK_LOGICAL_CONJUNCTION,
TK_CLOSING_PARENTHESIS => TK_END | TK_CLOSING_PARENTHESIS | TK_LOGICAL_DISJUNCTION | TK_LOGICAL_CONJUNCTION,
TK_LOGICAL_DISJUNCTION => TK_LOGICAL_NEGATION | TK_OPENING_PARENTHESIS | TK_COLUMN_NAME,
TK_LOGICAL_CONJUNCTION => TK_LOGICAL_NEGATION | TK_OPENING_PARENTHESIS | TK_COLUMN_NAME,
TK_LOGICAL_NEGATION => TK_OPENING_PARENTHESIS | TK_COLUMN_NAME,
TK_OPENING_PARENTHESIS => TK_OPENING_PARENTHESIS | TK_COLUMN_NAME
];
const COLUMN_NAMES = ['family', 'conditions_of_use', 'taxonomical_group'];
const ERROR_MESSAGES = [
ERR_UNEXPECTED_CHAR => 'caractère inattendu à la position %d.',
ERR_UNCLOSED_STRING => 'chaîne de caractères non fermée à la position %d.',
ERR_BAD_UTF8 => 'erreur d\'encodage UTF-8 à la position %d.',
ERR_EMPTY_STRING => 'chaîne vide à la position %d.', // *****
ERR_UNEXPECTED_TOKEN => 'élément inattendu à la position %d.',
ERR_UNEXPECTED_CLOSING_PARENTHESIS => 'parenthèse fermante inattendue à la position %d.',
ERR_MISSING_CLOSING_PARENTHESIS => 'parenthèse fermante manquante à la position %d.',
ERR_UNKNOW_COLUMN_NAME => 'nom de colonne inconnu à la position %d.',
ERR_EMPTY_QUERY => 'requête vide à la position %d.'
];
const TOKEN2SQL = [
TK_EQUAL => ' = ',
TK_NOT_EQUAL => ' <> ',
TK_CLOSING_PARENTHESIS => ')',
TK_OPENING_PARENTHESIS => '(',
TK_LOGICAL_DISJUNCTION => ' OR ',
TK_LOGICAL_CONJUNCTION => ' AND ',
TK_LOGICAL_NEGATION => 'NOT ',
TK_STRING => '?' // placeholder
];
function tokenize(string $str) {
$pattern = <<<'REGEX'
~
\s+ (*:TK_WHITESPACES)
| \( (*:TK_OPENING_PARENTHESIS)
| \) (*:TK_CLOSING_PARENTHESIS)
| \b ET \b (*:TK_LOGICAL_CONJUNCTION)
| \b OU \b (*:TK_LOGICAL_DISJUNCTION)
| \b NON \b (*:TK_LOGICAL_NEGATION)
| \B " [^"\\]* (?s: \\ . [^"\\]* )*
(?: " \B (*:TK_STRING) | " (*:ERR_UNEXPECTED_CHAR) | \z (*:ERR_UNCLOSED_STRING) )
| \b \w+ (*:TK_COLUMN_NAME)
| = (*:TK_EQUAL)
| (?: != | <> ) (*:TK_NOT_EQUAL)
| \z (*:TK_END)
| (*COMMIT) (*FAIL) # UNEXPECTED CHARACTER ⮕ tokenization aborted
~xu
REGEX;
$count = preg_match_all($pattern, $str, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
if ( preg_last_error() === PREG_BAD_UTF8_ERROR ) {
preg_match('~(?: # séquences UTF-8 valides
[\x00-\x7F]
| [\xC2-\xDF][\x80-\xBF]
| \xE0[\xA0-\xBF][\x80-\xBF]
| \xED[\x80-\x9F][\x80-\xBF]
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
| \xF0[\x90-\xBF][\x80-\xBF]{2}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
| [\xF1-\xF3][\x80-\xBF]{3}
)*~x', $str, $m);
return [ 'error' => ['type' => ERR_BAD_UTF8, 'offset' => strlen($m[0])] ];
}
if ( $count === 0 ) // (*COMMIT) (*FAIL) a été atteint dés la première position
return [ 'error' => ['type' => ERR_UNEXPECTED_CHAR, 'offset' => 0] ];
$errors = array_filter($matches, fn($m) => constant($m['MARK']) & ERROR);
if ( !empty($errors) ) { // la fin a été atteinte mais il y a des marqueurs d'erreurs parmi les tokens
$error = array_shift($errors);
list($match, $offset) = $error[0];
return [ 'error' => ['type' => constant($error['MARK']), 'offset' => $offset + strlen($match)] ];
}
$lastMatch = end($matches);
if ( constant($lastMatch['MARK']) !== TK_END ) { // (*COMMIT) (*FAIL) a été atteint avant la fin
list($match, $offset) = $lastMatch[0];
return [ 'error' => ['type' => ERR_UNEXPECTED_CHAR, 'offset' => $offset + strlen($match)] ];
}
if ( $count === 1 ) // l'unique token est TK_END, la chaîne est vide
return [ 'error' => ['type' => ERR_EMPTY_STRING, 'offset' => 0] ];
return [
'error' => ERR_NONE,
'tokens' => array_map(
fn($m) => [ 'value' => $m[0][0], 'offset' => $m[0][1], 'type' => constant($m['MARK']) ],
$matches
)
];
}
function checkLogic($tokens) {
$parenthesis = 0;
$current = current($tokens);
if ( $current['type'] === TK_END )
return [ 'type' => ERR_EMPTY_QUERY, 'offset' => $current['offset'] ];
do {
if ( $current['type'] === TK_COLUMN_NAME && !in_array($current['value'], COLUMN_NAMES) )
return [ 'type' => ERR_UNKNOW_COLUMN_NAME, 'offset' => $current['offset'] ];
if ( $current['type'] === TK_OPENING_PARENTHESIS )
$parenthesis++;
elseif ( $current['type'] === TK_CLOSING_PARENTHESIS && --$parenthesis < 0 )
return [ 'type' => ERR_UNEXPECTED_CLOSING_PARENTHESIS, 'offset' => $current['offset'] ];
$next = next($tokens);
if ( RULES[$current['type']] & $next['type'] ) // le token courant est suivi par un token autorisé
$current = $next;
else
return [ 'type' => ERR_UNEXPECTED_TOKEN, 'offset' => $next['offset'] ];
} while ( $next['type'] !== TK_END );
return $parenthesis ? [ 'type' => ERR_MISSING_CLOSING_PARENTHESIS, 'offset' => $current['offset'] ]
: true ;
}
function displayError($error, $query) {
$position = grapheme_strlen(substr($query, 0, $error['offset']));
$format = ERROR_MESSAGES[$error['type']]."\n%s\n" . str_repeat(' ', $position) . '^';
printf($format, $position, $query);
}
function buildQuery($tokens, $prefix = '') {
$SQLQuery = $prefix;
$params = [];
foreach ($tokens as $token) {
switch($token['type']) {
case TK_COLUMN_NAME:
$SQLQuery .= $token['value'];
break;
case TK_END: break;
case TK_STRING:
$param = substr($token['value'], 1, -1);
$params[] = strtr($param, ['\\\\' => '\\\\', '\\"' => '"']);
default:
$SQLQuery .= TOKEN2SQL[$token['type']];
}
}
return [$SQLQuery, $params];
}
$query = "".'family="_\\"bîd'."u\xCC\x8A".'le\\"_" OU (((taxonomical_group="machin")))';
$tokenization = tokenize($query);
$error = $tokenization['error'];
if ( $error !== ERR_NONE ) {
displayError($error, $query);
} else {
$tokens = array_filter($tokenization['tokens'], fn($token) => $token['type'] !== TK_WHITESPACES);
if ( true !== $error = checkLogic($tokens) ) {
displayError($error, $query);
} else {
$prefix = 'SELECT * FROM matable WHERE ';
list($SQLQuery, $params) = buildQuery($tokens, $prefix);
echo "requête: $query", PHP_EOL,
"requête préparée: $SQLQuery", PHP_EOL,
'paramètres: ', print_r($params, true);
// $sth = $dbh->prepare($SQLQuery);
// $sth->execute($params);
}
}
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).
Version System time (s) User time (s) Memory (MiB) 8.4.12 0.016 0.006 20.72 8.4.11 0.005 0.006 22.84 8.4.10 0.017 0.004 18.44 8.4.9 0.011 0.011 19.04 8.4.8 0.013 0.007 18.16 8.4.7 0.006 0.003 18.47 8.4.6 0.009 0.013 20.74 8.4.5 0.011 0.010 22.36 8.4.4 0.003 0.006 19.84 8.4.3 0.007 0.014 20.43 8.4.2 0.013 0.006 18.02 8.4.1 0.006 0.003 19.66 8.3.25 0.016 0.004 18.91 8.3.24 0.016 0.004 17.03 8.3.23 0.011 0.009 16.70 8.3.22 0.004 0.005 19.08 8.3.21 0.008 0.007 16.84 8.3.20 0.006 0.003 16.83 8.3.19 0.009 0.008 17.52 8.3.18 0.011 0.008 18.92 8.3.17 0.013 0.006 17.14 8.3.16 0.013 0.006 17.07 8.3.15 0.017 0.003 17.44 8.3.14 0.003 0.017 18.85 8.3.13 0.007 0.003 16.97 8.3.12 0.016 0.003 20.88 8.3.11 0.004 0.004 17.00 8.3.10 0.006 0.003 17.14 8.3.9 0.005 0.003 26.77 8.3.8 0.006 0.003 17.00 8.3.7 0.010 0.010 18.52 8.3.6 0.010 0.010 18.68 8.3.5 0.013 0.003 18.61 8.3.4 0.015 0.003 19.52 8.3.3 0.011 0.004 19.33 8.3.2 0.008 0.000 24.18 8.3.1 0.008 0.000 24.66 8.3.0 0.005 0.003 26.16 8.2.29 0.012 0.007 20.34 8.2.28 0.007 0.003 18.73 8.2.27 0.016 0.004 16.87 8.2.26 0.016 0.003 19.19 8.2.25 0.008 0.000 17.28 8.2.24 0.007 0.003 18.82 8.2.23 0.008 0.000 20.94 8.2.22 0.006 0.003 24.06 8.2.21 0.009 0.000 26.77 8.2.20 0.003 0.007 16.88 8.2.19 0.011 0.007 17.00 8.2.18 0.006 0.009 25.92 8.2.17 0.012 0.003 19.19 8.2.16 0.011 0.004 22.96 8.2.15 0.005 0.003 25.66 8.2.14 0.000 0.008 24.66 8.2.13 0.008 0.000 26.16 8.2.12 0.003 0.006 19.74 8.2.11 0.011 0.007 22.25 8.2.10 0.004 0.008 20.01 8.2.9 0.000 0.008 18.25 8.2.8 0.000 0.008 19.15 8.2.7 0.000 0.008 17.88 8.2.6 0.006 0.003 17.75 8.2.5 0.000 0.009 17.98 8.2.4 0.004 0.004 18.59 8.2.3 0.008 0.000 18.39 8.2.2 0.005 0.003 20.53 8.2.1 0.005 0.003 18.22 8.2.0 0.004 0.004 19.42 8.1.33 0.015 0.008 16.86 8.1.32 0.005 0.005 16.60 8.1.31 0.012 0.003 17.00 8.1.30 0.004 0.007 18.21 8.1.29 0.003 0.006 30.84 8.1.28 0.007 0.007 25.92 8.1.27 0.007 0.000 23.99 8.1.26 0.004 0.004 26.35 8.1.25 0.000 0.008 28.09 8.1.24 0.007 0.007 23.82 8.1.23 0.007 0.004 19.09 8.1.22 0.004 0.004 18.97 8.1.21 0.006 0.003 18.77 8.1.20 0.008 0.000 17.72 8.1.19 0.008 0.000 17.48 8.1.18 0.004 0.004 18.10 8.1.17 0.008 0.000 17.62 8.1.16 0.003 0.005 19.00 8.1.15 0.003 0.006 18.91 8.1.14 0.000 0.008 17.81 8.1.13 0.000 0.007 19.09 8.1.12 0.000 0.010 17.82 8.1.11 0.004 0.007 17.83 8.1.10 0.003 0.006 17.81 8.1.9 0.004 0.004 17.75 8.1.8 0.000 0.010 17.76 8.1.7 0.004 0.004 17.74 8.1.6 0.003 0.006 17.86 8.1.5 0.008 0.000 17.87 8.1.4 0.006 0.003 17.77 8.1.3 0.017 0.003 17.94 8.1.2 0.019 0.006 17.95 8.1.1 0.022 0.004 17.84 8.1.0 0.024 0.000 17.77 8.0.30 0.003 0.005 18.77 8.0.29 0.006 0.003 17.13 8.0.28 0.007 0.000 18.68 8.0.27 0.004 0.004 17.12 8.0.26 0.003 0.003 18.53 8.0.25 0.000 0.008 17.28 8.0.24 0.007 0.000 17.33 8.0.23 0.004 0.004 17.35 8.0.22 0.004 0.004 17.29 8.0.21 0.000 0.007 17.37 8.0.20 0.000 0.008 17.24 8.0.19 0.004 0.004 17.30 8.0.18 0.004 0.004 17.35 8.0.17 0.003 0.006 17.34 8.0.16 0.014 0.007 17.23 8.0.15 0.015 0.005 17.23 8.0.14 0.017 0.003 17.14 8.0.13 0.010 0.010 17.20 8.0.12 0.016 0.004 17.18 8.0.11 0.009 0.009 17.38 8.0.10 0.010 0.010 17.37 8.0.9 0.012 0.008 17.36 8.0.8 0.016 0.003 17.38 8.0.7 0.014 0.006 17.35 8.0.6 0.015 0.005 17.11 8.0.5 0.014 0.006 17.28 8.0.3 0.020 0.000 17.35 8.0.2 0.016 0.005 17.43 8.0.1 0.011 0.007 17.25 7.4.33 0.003 0.003 15.55 7.4.32 0.003 0.005 16.95 7.4.30 0.003 0.003 17.00 7.4.29 0.007 0.000 16.78 7.4.28 0.018 0.003 16.99 7.4.27 0.014 0.007 16.98 7.4.26 0.016 0.006 16.98 7.4.25 0.010 0.010 16.91 7.4.24 0.021 0.000 16.96 7.4.23 0.010 0.008 16.82 7.4.22 0.014 0.004 16.95 7.4.21 0.015 0.006 16.83 7.4.20 0.015 0.005 16.99 7.4.19 0.016 0.004 16.89 7.4.18 0.013 0.007 16.75 7.4.16 0.015 0.004 16.89 7.4.15 0.014 0.007 16.79 7.4.14 0.015 0.003 16.82 7.4.13 0.017 0.003 16.92 7.4.12 0.017 0.003 16.89 7.4.11 0.014 0.005 16.81 7.4.10 0.014 0.005 16.79 7.4.9 0.017 0.002 16.72 7.4.8 0.015 0.006 16.86 7.4.7 0.015 0.004 16.74 7.4.6 0.017 0.000 16.86 7.4.5 0.018 0.000 16.77 7.4.4 0.012 0.006 16.66 7.4.3 0.016 0.003 16.73 7.4.2 0.011 0.008 16.75 7.4.1 0.014 0.004 16.86 7.4.0 0.007 0.011 16.89
preferences:dark mode live preview ace vim emacs key bindings
103.68 ms | 403 KiB | 5 Q