From 6e2681528377b7bda3199b683f0aaa90de010fec Mon Sep 17 00:00:00 2001 From: Christian Schneider Date: Fri, 9 Nov 2007 18:40:56 +0000 Subject: Added it_autoprepend.php, it_syntaxconverter.class and convertsyntax.php --- convertsyntax.php | 43 +++++ it_auto_prepend.php | 183 ++++++++++++++++++++ it_syntaxconverter.class | 430 +++++++++++++++++++++++++++++++++++++++++++++++ itools.lib | 1 + 4 files changed, 657 insertions(+) create mode 100755 convertsyntax.php create mode 100644 it_auto_prepend.php create mode 100755 it_syntaxconverter.class diff --git a/convertsyntax.php b/convertsyntax.php new file mode 100755 index 0000000..282b774 --- /dev/null +++ b/convertsyntax.php @@ -0,0 +1,43 @@ +#!/usr/bin/env php +changes) + { + if ($opts['verbose']) + echo "$file contains $converter->changes new syntax elements\n"; + + $result++; + } + } + else + { + echo $converter->output; + + if ($opts['verbose'] && $converter->changes) + fputs(fopen("php://stderr", "w"), "$converter->changes changes made to $file\n"); + } +} + +exit($result); diff --git a/it_auto_prepend.php b/it_auto_prepend.php new file mode 100644 index 0000000..3f69096 --- /dev/null +++ b/it_auto_prepend.php @@ -0,0 +1,183 @@ +. +*/ + +define('IT_CONVERT_DIR', "/tmp/it_syntaxconverter"); + +#$debug_itclassloader = true; +it_initialize(); + +function it_initialize() +{ + static $it_initrecursion; + + if (!$it_initrecursion++) + { + if ($_SERVER['REMOTE_ADDR']) # Web? + { + $GLOBALS['ULTRAHOME'] = dirname($_SERVER['SCRIPT_FILENAME']); + umask(0002); # Work around bugs.php.net/bug.php?id=28401 + $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']); + } + else # Shell + $GLOBALS['ULTRAHOME'] = dirname(preg_match('|^/|', $argv[0]) ? $argv[0] : getcwd() . '/' . $argv[0]); + + $needsconvert = !@eval("return is_array(42=>69,);"); # Check if PHP is patched to support our syntax, see http://cschneid.com/php/ + + $include_path = ini_get('include_path'); + $it_path = dirname(__FILE__); + + if ($autoloader = function_exists('spl_autoload_register') && spl_autoload_register('it_classloader')) + { + ini_set('include_path', "$it_path:$include_path"); + require_once("$it_path/auto_prepend.php"); + } + else + { + ini_set('include_path', "$it_path/..:$include_path"); + require("itools.lib"); # PHP 4 fallback + } + + @set_error_handler("it_errorhandler", E_USER_ERROR | E_RECOVERABLE_ERROR | E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE); + ini_set('include_path', IT_CONVERT_DIR . ":$it_path:$include_path"); + $user_includes = explode(":", $include_path); + + # XXX Note: Comment this out if you want system wide include path converted and auto_prepend.php considered + $user_includes = array_diff($user_includes, array_diff(explode(":", get_cfg_var('include_path')), array("."))); + + foreach (array_reverse($user_includes) as $include) + { + if ($include == ".") + $include = dirname($_SERVER['SCRIPT_FILENAME']); + + if ($needsconvert) + it_convert($include); + + if (file_exists($autoprepend = "$include/auto_prepend.php")) + { + if ($needsconvert && ($include != $it_path)) + $autoprepend = it_convert($autoprepend); + + require_once($autoprepend); + } + } + + if (!isset($GLOBALS['it_html'])) + new it_html; + + if ($needsconvert) + { + # Convert syntax and start + $converted = it_convert($_SERVER['SCRIPT_FILENAME']); + + /* XXX Disabled as DB not always there + if (!$autoloader) # PHP 4 fallback + it_dbi::createclasses(); + */ + + require($converted); + exit; + } + } +} + +function it_convert($source) +{ + if (is_dir($source) && !is_link($source)) + { + foreach (glob("$source/*") as $file) + it_convert($file); + } + else if (is_readable($source)) + { + $converted = IT_CONVERT_DIR . "/$source"; + + if (@filemtime($converted) < filemtime($source)) + { + $converter = new it_syntaxconverter(file_get_contents($source)); + $parts = explode("/", dirname($converted)); + + for ($i = 1; $i <= count($parts); $i++) + @mkdir(join("/", array_slice($parts, 0, $i))); + + if ($output = fopen($converted, "w")) + { + fputs($output, $converter->output); + fclose($output); + chmod($converted, 0666); + } + + clearstatcache(); + } + } + + return $converted; +} + +function it_classloader($classname) +{ + if ($file = @fopen("$classname.class", "r", true)) # Check for file in include path, do not use @include to get failures on inheritance + { + include("$classname.class"); + fclose($file); + } + else + it_dbi::createclass($classname); + + EDC('ultraclassloader', $classname, it_debug::backtrace()); +} + +function it_errorhandler($errno, $errstr, $errfile, $errline, $errcontext) +{ + if ($result = error_reporting() & $errno) # Is this error enabled? + { + static $it_errnames = array(); + + if (!$it_errnames) + { + foreach (get_defined_constants() as $name => $no) + { + if (preg_match('/^E_/', $name)) + $it_errnames[$no] = "$name: "; + } + } + + $error = array( + 'title' => $it_errnames[$errno] . $errstr, + 'locals' => $errcontext, + 'file' => $errfile, + 'line' => $errline, + 'backtraceskip' => 1, + ); + + switch ($errno) + { + case E_USER_ERROR: case E_RECOVERABLE_ERROR: + it::fatal($error); + break; + + default: + it::error($error); + break; + } + } + + return $result; # True means do not execute standard PHP error handler +} diff --git a/it_syntaxconverter.class b/it_syntaxconverter.class new file mode 100755 index 0000000..ef0b5f3 --- /dev/null +++ b/it_syntaxconverter.class @@ -0,0 +1,430 @@ +. +*/ + +class it_syntaxconverter +{ + var $mode; # Either 'old', 'new', 'test' + var $changes = 0; + + var $whitespace = array + ( + T_WHITESPACE => true, + T_COMMENT => true, + ); + + var $arrayoptional = array + ( + T_VARIABLE => true, + T_STRING => true, + ); + +# +# Constructor +# +function it_syntaxconverter($string, $mode = "old") +{ + if (defined('T_ML_COMMENT')) + $this->whitespace[T_ML_COMMENT] = true; # PHP4 + + if (defined('T_DOC_COMMENT')) + $this->whitespace[T_DOC_COMMENT] = true; # PHP5 + + $this->input = $string; + $this->mode = $mode; + $this->tokens = token_get_all($this->input); + $this->count = count($this->tokens); + $this->position = 0; + $this->old = $this->parse(); + $this->new = $this->convert($this->old); + $this->output = $this->text($this->new); +} + +# +# Build parse tree from source string +# +function parse() +{ + $result = array(); + + while ($this->position < $this->count) + { + $token = $this->tokens[$this->position++]; + + if (is_string($token)) + { + if ($token == '(') + $result[] = array('LIST', $this->parse_list()); + else + $result[] = array('TEXT', $token); + } + else + $result[] = $token; + } + + return $result; +} + +function parse_list() +{ + $result = array(); + + while ($this->position < $this->count) + { + list($token, $element) = $this->parse_element(); + $result[] = $element; + + if ($token == ')') + break; + } + + return $result; +} + +function parse_element() +{ + $result = array(); + + while ($this->position < $this->count) + { + $token = $this->tokens[$this->position++]; + + if (is_string($token)) + { + if ($token == '(') + $result[] = array('LIST', $this->parse_list()); + else if (($token == ',') || ($token == ')')) + break; + else + $result[] = array('TEXT', $token); + } + else + $result[] = $token; + } + + return array($token, $result); +} + + +# +# Modify parse tree to old syntax +# +function convert($tokens) +{ + $result = array(); + $op = ''; + + foreach ($tokens as $token) + { + list($id, $elements) = $token; + + if ($id == 'LIST') + { + if (empty($this->arrayoptional[$op])) + { + $list = array(); + + foreach ($elements as $element) + $list[] = $this->convert($element); + + $result[] = array($id, $list); + } + else + $result[] = array('LIST', $this->convert_list($elements)); + } + else + $result[] = $token; + + if (empty($this->whitespace[$id])) + $op = $id; + } + + return $result; +} + +function convert_list($elements) +{ + if ($this->mode != 'new') + { + $result = $this->convert_list_namedparameters($elements); + $result = $this->convert_list_trailingcomma($result); + } + else + $result = $this->convert_list_arrayparameters($elements); + + return $result; +} + +# +# Remove array() around named parameter lists +# +function convert_list_arrayparameters($elements) +{ + $result = array(); + $locked = false; + + foreach ($elements as $element) + { + $element = $this->convert($element); + + if ($locked) + { + $result[] = $element; + $locked = false; + } + else + { + $elementlist = $this->convert_pure_assoc_array($element); + + foreach ($elementlist as $element) + $result[] = $element; + + $locked = count($elementlist) != 1; + } + } + + return $result; +} + +function convert_pure_assoc_array($tokens) +{ + $result = array(); + $element = array(); + $array = false; + $locked = false; + + foreach ($tokens as $token) + { + list($id, $elements) = $token; + + if ($array) + { + if (($id == 'LIST') && $this->only_double_arrow($elements)) + { + foreach ($elements as $listelements) + { + $listelements = $this->convert($listelements); + + foreach ($listelements as $listelement) + $element[] = $listelement; + + $result[] = $element; + $element = array(); + } + + # Continue with last element + $element = array_pop($result); + } + else + { + $element[] = $array; + $element[] = $token; + } + + $array = false; + $locked = true; + } + else if (!$locked && ($id == T_ARRAY)) + { + $array = $token; + } + else if (empty($this->whitespace[$id])) + { + if ($locked) + { + # Encountered something after array, abort + $result = array(); + $element = $tokens; + break; + } + else + { + $element[] = $token; + $locked = true; + } + } + else + $element[] = $token; + } + + if ($element) + $result[] = $element; + + return $result; +} + +function only_double_arrow($elements) +{ + $result = $elements; # Has to contain elements, empty is false + + foreach ($elements as $element) + { + if (!$this->has_double_arrow($element)) + { + $result = false; + break; + } + } + + return $result; +} + +# +# Add array() around named parameter lists +# +function convert_list_namedparameters($elements) +{ + $namedparams = array(); + + foreach ($elements as $element) + { + $element = $this->convert($element); + + if (!$this->has_double_arrow($element)) + { + if ($namedparams) + { + $result[] = $this->make_array($namedparams); + $namedparams = array(); + } + + $result[] = $element; + } + else + $namedparams[] = $element; + } + + if ($namedparams) + $result[] = $this->make_array($namedparams); + + return $result; +} + +# +# Remove trailing comma by adding content to previous element +# +function convert_list_trailingcomma($elements) +{ + $result = $elements; + $last = count($result) - 1; + + if (($last > 0) && $this->is_empty($result[$last])) + { + $this->changes++; + $element = array_pop($result); + $last--; + foreach ($element as $token) + $result[$last][] = $token; + } + + return $result; +} + +function has_double_arrow($element) +{ + $result = false; + + foreach ($element as $token) + { + list($id, $text) = $token; + + if ($id == T_DOUBLE_ARROW) + { + $result = true; + break; + } + } + + return $result; +} + +function make_array($namedparams) +{ + $result = array(); + $head = array(); + $body = array(); + + $this->changes++; + $namedparam = $namedparams[0]; + + foreach ($namedparam as $token) + { + list($id, $text) = $token; + + if ($body || (($id != 'LIST') && empty($this->whitespace[$id]))) + $body[] = $token; + else + $result[] = $token; + } + + $namedparams[0] = $body; + $result[] = array(T_ARRAY, "array"); + $result[] = array('LIST', $namedparams); + + return $result; +} + +function is_empty($element) +{ + $result = true; + + foreach ($element as $token) + { + list($id, $text) = $token; + + if (empty($this->whitespace[$id])) + { + $result = false; + break; + } + } + + return $result; +} + + +# +# Convert parse tree back to string +# +function text($tokens) +{ + $result = ""; + + foreach ($tokens as $token) + { + list($id, $data) = $token; + + if ($id == 'LIST') + { + $list = array(); + + foreach ($data as $element) + $list[] .= $this->text($element); + + $result .= "(" . join(",", $list) . ")"; + } + else + $result .= $data; + } + + return $result; +} + +} diff --git a/itools.lib b/itools.lib index da3f0f5..3b6a2f1 100644 --- a/itools.lib +++ b/itools.lib @@ -27,6 +27,7 @@ require_once 'itools/it_html.class'; require_once 'itools/itjs.class'; require_once 'itools/it_mail.class'; require_once 'itools/it_session.class'; +require_once 'itools/it_syntaxconverter.class'; require_once 'itools/it_text.class'; require_once 'itools/it_url.class'; require_once 'itools/it_user.class'; -- cgit v1.2.3