]> git.nbdom.net Git - nb.git/commitdiff
lib/: php perl ruby pyhton
authorNicolas Boisselier <nicolas.boisselier@gmail.com>
Fri, 27 Mar 2015 21:15:00 +0000 (21:15 +0000)
committerNicolas Boisselier <nicolas.boisselier@gmail.com>
Fri, 27 Mar 2015 21:15:00 +0000 (21:15 +0000)
lib/php/db.php [new file with mode: 0644]
lib/php/default.php [new file with mode: 0644]
lib/php/gpio [new submodule]
lib/php/page.php [new file with mode: 0644]

diff --git a/lib/php/db.php b/lib/php/db.php
new file mode 100644 (file)
index 0000000..e445fe3
--- /dev/null
@@ -0,0 +1,668 @@
+<?php
+/*****************************************************************************
+
+  Class DB
+  Render different output format ex: html, xml, yaml, csv
+  from any database
+
+*****************************************************************************/
+class db {
+  var $tables = array();
+  var $conn;
+  var $type;
+  var $help_criterias = array(
+    ' * or % for wildcar',
+    ' ! to negate',
+    ' ~ for regex',
+    ' combine criterias with OR/AND',
+  );
+
+  function db($pdo) {
+    $this->conn = new PDO($pdo);
+    $this->type = strtolower(preg_replace('/^([^:]+):.*$/','\1',$pdo));
+
+    if ($this->type == 'sqlite') {
+
+      $this->conn->sqliteCreateFunction('regexp',
+        function ($pattern, $data, $delimiter = '~', $modifiers = 'isuS') {
+          if (isset($pattern, $data) === true) {
+              return (preg_match(sprintf('%1$s%2$s%1$s%3$s', $delimiter, $pattern, $modifiers), $data) > 0);
+          }
+          return null;
+        }
+      );
+
+      $this->conn->sqliteCreateFunction('to_char',
+        function ($value,$format) {
+          $replace = array(
+              'YYYY' => '%Y',
+              'MM' => '%m',
+              'WW' => '%W',
+              'DD' => '%d',
+          );
+          $format = str_replace(array_keys($replace),array_values($replace),$format);
+          return strftime($format,strtotime($value));
+        }
+      ,2);
+
+    }
+
+  }
+
+/*
+  function database() {
+    $rows = $this->db->query('PRAGMA database_list');
+  }
+*/
+
+  function tables() {
+    if (!$this->tables) {
+
+      if ($this->type == 'sqlite') {
+        $sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name";
+      } elseif ($this->type == 'pgsql') {
+        $sql = "SELECT table_name FROM information_schema.tables WHERE table_type in('BASE TABLE','LOCAL TEMPORARY') AND table_schema NOT IN ('pg_catalog', 'information_schema')";
+      } else {
+        err('db.tables(): Unknow db type: '.$this->type);
+        return array();
+      }
+
+      $rows = $this->conn->query($sql);
+      foreach ($rows as $row) {
+        $this->tables[] = current($row);
+      }
+
+    }
+    return $this->tables;
+
+  }
+
+  function rowCount($st) {
+    if (!$st) return null;
+    if ($this->type != 'sqlite') return $st->rowCount();
+    $sql = $st->queryString;
+    return $this->conn->query("SELECT count(*) FROM ($sql) count",PDO::FETCH_COLUMN,0)->fetch();
+  }
+
+  function help($tables=null) {
+    header('Content-type: text/plain');
+
+    if ($tables === null) $tables = $this->tables();
+    $tables = join('',ar_map('"  ".$a."\n"',$tables));
+
+    $criterias = join('',ar_map('"  ".$a."\n"',$this->help_criterias));
+
+    return <<<EOF
+Criterias:
+$criterias
+Tables
+$tables
+Extras:
+  - op     = [OR|AND] criterias operator
+  - sort   = field+DESC
+  - format = [xml|yaml|csv|table|row]
+  - header = [1|0] Print header fields (default: 1)
+
+EOF;
+  }
+
+  function print_header($type) {
+
+    if ($_REQUEST['format']=='csv') {
+      header('Content-type: text/plain');
+
+    } elseif ($_REQUEST['format']=='yaml') {
+      header('Content-type: text/yaml');
+
+    } elseif ($_REQUEST['format']=='xml') {
+      header('Content-type: text/xml');
+
+    } else {
+
+      @$GLOBALS['PAGE']['is_text'] = false;
+      return false;
+    }
+
+    @$GLOBALS['PAGE']['is_text'] = true;
+    return true;
+  }
+
+  function initParams($tables=null) {
+    if (empty($_REQUEST)) $_REQUEST = array();
+    if ($tables === null) $tables = $this->tables();
+
+    #$_REQUEST['table'] = @$_REQUEST['table'] ? preg_replace('/^([\w\d_-]+).*$/','\1',$_REQUEST['table']) : $tables[0];
+    $_REQUEST['table'] = @$_REQUEST['table'] ? $_REQUEST['table'] : ($tables === null ? '' : $tables[0]);
+    if (@$_REQUEST['sql']) $_REQUEST['table'] = $_REQUEST['sql'];
+
+    $_REQUEST['limit'] = @$_REQUEST['limit']
+      ? preg_replace('/^(\d+)(,\d+).*$/','\1\2',$_REQUEST['limit'])
+      : ( preg_match('/^(curl|lynx|lwp)/',@$_SERVER['HTTP_USER_AGENT']) ? 999999 : 50 )
+    ;
+
+    $_REQUEST['format'] = @$_REQUEST['format'] ? $_REQUEST['format'] : 'table';
+
+    #$_REQUEST['op'] = strtoupper(@$_REQUEST['op']) == 'AND' ? 'AND' : 'OR';
+    $_REQUEST['op'] = strtoupper(@$_REQUEST['op']) == 'OR' ? 'OR' : 'AND';
+
+    @$GLOBALS['PAGE']['js_code'] = 'var sem_ui_tables = ['.join(',',ar_map('str_quote($a)',$tables)).']; var sem_ui_table = '.str_quote(@$_REQUEST['table']).';';
+
+    }
+  }
+
+class table {
+
+  var $name;
+  var $db;
+  var $fields = array();
+  var $extras = array();
+  var $params = array(
+    'table',
+    'limit',
+    'debug',
+  );
+
+  function table($name,$opt=array()) {
+
+    // Connection
+    if (@$opt['db']) {
+      $this->db = @$opt['db'];
+    } else {
+      $this->db = new db();
+    }
+
+    // Table could be a select
+    if (stripos($name,'SELECT ')===0) {
+      $this->db->conn->query("CREATE TEMPORARY TABLE _query_ AS $name");
+      $name = '_query_';
+    } elseif (preg_match('/\b(\.import|LOAD DATA|COPY|INSERT|REPLACE|DELETE|TRUNCATE|CREATE|DROP|ALERT)\b/',$name)) {
+      bye("Query not Allowed !");
+    }
+
+    $this->name = $name;
+
+    if (@$opt['extras']) $this->add_extras($opt['extras']);
+
+    return array();
+  }
+
+  function fields() {
+    if (!$this->fields) {
+
+      if ($this->db->type == 'sqlite') {
+        $sql = "PRAGMA table_info('$this->name')";
+
+      } elseif ($this->db->type == 'pgsql') {
+        $sql = "SELECT a.attname as name, pg_catalog.format_type(a.atttypid, a.atttypmod) as type, case a.attnotnull when 't' then 1 else 0 end as notnull FROM pg_catalog.pg_attribute a WHERE a.attrelid = (SELECT c.oid FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname='$this->name' AND pg_catalog.pg_table_is_visible(c.oid) ) AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum";
+
+      } else {
+        err('table.fields(): Unknow db type: '.$this->db->type);
+        return array();
+
+      }
+
+      $rows = $this->db->conn->query($sql);
+
+      $rows->setFetchMode(PDO::FETCH_ASSOC);
+
+      foreach ($rows as $row) {
+
+        $this->fields[$row['name']]['type'] = $row['type'];
+        $this->fields[$row['name']]['null'] = $row['notnull'] == '0' ? 1 : 0;
+        $this->fields[$row['name']]['extra'] = null;
+
+      }
+
+    }
+
+    return $this->fields;
+  }
+
+  function debug($msg,$level=0) {
+    if ($level and $level>DEBUG) return;
+
+    $msg = is_scalar($msg) ? $msg : print_r($msg,true);
+    if (@$GLOBALS['PAGE']['is_text']) {
+      echo "DEBUG: $msg\n";
+      return;
+    }
+    echo '<pre class="debug">'
+      .(@$_SERVER['HTTP_HOST'] ? htmlentities($msg) : $msg)
+    .'</pre>'.PHP_EOL;
+
+  }
+
+  function url_params($k='',$v='') {
+
+    $params = array();
+
+    foreach (array_merge(
+      $this->params,
+      array_keys($this->fields())
+    ) as $f) {
+
+      if (@strcmp($_REQUEST[$f],'')==0) continue;
+      $params[$f] = $_REQUEST[$f];
+
+    }
+
+    if ($k) {
+
+      if (strcmp($v,'')==0) {
+        unset($params[$k]);
+      } else {
+        $params[$k] = $v;
+      }
+
+    }
+
+    $flat = array();
+    foreach ($params as $k=>$v) { $flat[] = $k.'='.urlencode($v); }
+    return $flat ? '?'. join('&amp;',$flat) : ''; 
+
+  }
+
+  function url_sort($name) {
+
+    $html = '';
+
+    # Asc
+    $sel = ( @$_REQUEST['sort']=="$name asc") ? " sel" : "";
+    $html .= '<a title="First In (asc)" class="sort asc'.$sel.'" href="'.$this->url_params("sort","$name asc").'">'
+      .'<span class="asc">&darr;</span>'
+    .'</a>';
+    $html .= '&nbsp;';
+
+    $html .= $name;
+
+    $html .= '&nbsp;';
+
+    # Desc
+    $sel = ( @$_REQUEST['sort']=="$name desc") ? " sel" : "";
+    $html .= '<a title="Last In (desc)" class="sort desc'.$sel.'" href="'.$this->url_params("sort","$name desc").'">'
+      .'<span class="desc">&uarr;</span>'
+    .'</a>';
+
+    return $html;
+
+  }
+
+  function nav($count,$tot,$limit) {
+
+    if ($count<$tot) {
+      list($x,$y) = strpos($limit,',')!==false
+        ? preg_split('/\s*,\s*/',$limit)
+        : array(0,$limit)
+      ;
+
+      $prev = $x - $y;
+      $next = $x + $y;
+
+      $this->debug("x=$x limit=$y prev=$prev next=$next tot=$tot",1);
+    } else {
+      $x = 0;
+      $y = $tot;
+      $prev = -1;
+      $next = 999999;
+    }
+
+    echo '<div class="nav" id="nav_bottom">';
+
+    if ($prev>=0) echo '<span class="prev"><a href="'.$this->url_params('limit',preg_replace('/^0,/','',"$prev,$y")).'">&lt;&lt;</a></span>&nbsp;';
+
+    echo '<span class="count">'.($tot ? ($x+1) : 0).' - '.($x+$y).' / '.$tot.' results</span>';
+
+    if ($next<$tot) echo '&nbsp;<span class="prev"><a href="'.$this->url_params('limit',"$next,$y").'">&gt;&gt;</a></span>';
+
+    echo '</div>'.PHP_EOL;
+  }
+
+  function form_criterias($opt=array()) {
+    
+    echo '<form class="criteria bgcolor border rad inline" method="get" action="">'.PHP_EOL;
+    echo '<div class="small help">Use: '.join(' | ',$this->db->help_criterias).'</div>'.PHP_EOL;
+
+    foreach ($this->params as $k) {
+      $v = @$_REQUEST[$k];
+      if ($k == 'limit') $v = '';
+      echo '<input type="hidden" name="'.$k.'" value="'.$v.'" />'.PHP_EOL; 
+    }
+
+    $criteria = array();
+    foreach ( array_keys($this->fields()) as $k ) {
+
+      $v = @$_REQUEST[$k];
+
+      $criteria[] = ''
+        . '<label>'.prettyText($k).':</label>'
+        . '<input type="text" id="'.$k.'" name="'.$k.'" value="'.$v.'" />'
+      ;
+
+    }
+
+    $criteria[] = html_select_array(array(
+      'AND',
+      'OR',
+    ),array(
+      'html' => 'name="op"',
+      'selected' => @$_REQUEST['op'],
+    ));
+
+    $criteria[] = html_select_array(array(
+      array('','HTML'),
+      array('csv','CSV'),
+      array('xml','XML'),
+      array('yaml','YAML'),
+    ),array(
+      'html' => 'name="format"',
+      'selected' => @$_REQUEST['format'],
+    ));
+    $criteria[] = '<input type="submit" class="button" value="GO"/>';
+
+    echo join(''.PHP_EOL,$criteria);
+
+    echo '</form>'.PHP_EOL;
+  }
+
+  function where_criterias() {
+    $having = $where = array();
+
+    foreach ($this->fields() as $k => $spec) {
+
+      // No empty values
+      $v = @$_REQUEST[$k];
+      if (strcmp($v,'')==0 or $v=='!' or $v=='~') continue;
+
+      // Equal / Not Equal
+      $equal = '=';
+      $not = strpos($v,'!')===0 ? 1 : 0;
+      if ($not) $v = substr($v,1);
+
+      // Regex
+      if (strpos($v,'~')===0) {
+        $v = substr($v,1);
+        $v = $this->db->conn->quote($v);
+        $equal = ' '.($not ? 'NOT ' : '').'REGEXP ';
+
+      // Text
+      } elseif (preg_match('/text|char|blob/',$spec['type'])
+        or !preg_match('/^[\d+\.]$/',$v) # text char in search
+      ) {
+
+        if (strtolower($v)=='null') $v = '';
+        #$k = "COLAESCE($k,'')";
+
+        // * -> %
+        $v = str_replace('*','%',$v);
+
+        $v = $this->db->conn->quote($v);
+        $equal = ' '.($not ? 'NOT ' : '').'LIKE ';
+
+      // Integer
+      } else {
+        if (preg_match('/date|time/',$spec['type'])) $v = $this->db->conn->quote($v);
+        if (strtolower($v)=='null') $v = '0';
+        $equal = $not ? '<>' : '=';
+
+      }
+
+      if (preg_match('/(LIKE|REGEXP) ..$/',"$equal$v")) {
+        $k = "COALESCE($k,".$this->db->conn->quote('').")";
+      }
+
+      if ($this->db->type == 'mysql' and $spec['extra']) {
+        $having[] = "$k$equal$v";
+      } elseif ($this->db->type == 'pgsql' and $spec['extra']) {
+        $where[] = $this->extras[$k]."$equal$v";
+      } else {
+        $where[] = "$k$equal$v";
+      }
+
+    }
+
+    $sql = '';
+    if ($where) $sql .= ' WHERE '.join(' '. @$_REQUEST['op'].' ',$where);
+    if ($having) $sql .= ' HAVING '.join(' '. @$_REQUEST['op'].' ',$having);
+    return $sql;
+
+  }
+
+  function add_extras($extras) {
+    $this->fields();
+
+    foreach ($extras as $k => $v) {
+
+      $this->fields[$k] = array(
+        'type' => 'text', 
+        'null' => 0,
+        'extra' => $v,
+      );
+
+      $this->extras[$k] = $v;
+
+    }
+
+  }
+
+  function select_extras() {
+
+    if (!$this->extras) return '';
+
+    $select = array();
+
+    foreach ($this->extras as $k => $v) {
+
+      $select[] = "$v AS ".($this->db->type == 'pgsql' 
+        ? '"'.str_replace('"','\"',$k).'"'
+        : $this->db->conn->quote($k)
+      );
+      /*
+      $select[] = "$v AS ".$this->db->conn->quote($k);
+      */
+
+      /*
+      $k = $this->db->conn->quote($k);
+      if ($this->db->type == 'pgsqpl') $k = str_replace(
+      $select[] = "$v AS $k"
+      */
+
+    }
+
+    return ','.join(',',$select);
+  }
+
+  /******************************************************************
+    Html Output
+  ******************************************************************/
+  function rows($opt=array()) {
+    
+    //
+    // Select
+    //
+    $sql = "SELECT *" . $this->select_extras();
+    $sql .= " FROM $this->name".$this->where_criterias();
+    #$this->debug($sql);
+    $this->debug($sql,1);
+
+    //
+    // Tot
+    //
+    $query = $this->db->conn->query("SELECT count(*) FROM ($sql) count",PDO::FETCH_COLUMN,0);
+    if (!$query) {
+      $err = $this->db->conn->errorInfo();
+      $err[] = $sql;
+      err(join(' | ',$err));
+      return $err[0];
+    }
+    $tot = $query->fetch();
+    #if (!$tot) return;
+
+    if (isset($_REQUEST['sort'])) $sql .= ' ORDER BY '.$_REQUEST['sort'];
+
+    if (isset($_REQUEST['limit'])) {
+      $limit = $_REQUEST['limit'];
+      $sql .= ' LIMIT '.$limit;
+    }
+
+    //
+    // Get results
+    //
+    $st = $this->db->conn->prepare($sql);
+    $st->execute();
+
+    if (@$GLOBALS['PAGE']['is_html']) echo '<div class="nav" id="nav_top"></div>'.PHP_EOL;
+
+    $format = ''.(isset($opt['format']) ? $opt['format'] : 'table');
+    $escape = preg_match('/^(table|row|xml)$/',$format) ? true : false;
+    if (preg_match('/^(1)?$/',@$_REQUEST['header'])) echo $this->{"rows_begin_$format"}($opt);
+
+    $count = 0;
+    while ($row = $st->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT)) {
+      $count ++;
+
+      foreach ($this->fields() as $f => $spec) {
+
+        if (!$spec['extra']) {
+          if ($escape) $row[$f] = htmlspecialchars($row[$f]);
+        }
+      /* only if in latin1
+        if ($_REQUEST['format'] == 'csv') {
+          $row[$f] = utf8_encode($row[$f]);
+        } elseif ($spec['extra']) {
+          $row[$f] = htmlentities($row[$f]);
+
+        }
+      */
+
+      }
+
+      echo $this->{"rows_rec_$format"}($row);
+
+    }
+
+    echo $this->{"rows_end_$format"}($opt);
+    if (@$GLOBALS['PAGE']['is_html']) echo $this->nav($count,$tot,$limit);
+
+    $st->closeCursor();
+
+    return $count;
+  }
+
+  /*-----------------------------------------------------------------
+    Yaml
+  -----------------------------------------------------------------*/
+  function rows_begin_yaml() {
+    $GLOBALS['yamls'] = array();
+    return '';
+  }
+
+  function rows_rec_yaml($row) {
+    #echo yaml_emit($row);
+    $GLOBALS['yamls'][] = $row;
+  }
+
+  function rows_end_yaml() {
+    return yaml_emit($GLOBALS['yamls']);
+    return '';
+  }
+
+  /*-----------------------------------------------------------------
+    Xml
+  -----------------------------------------------------------------*/
+  function rows_begin_xml() {
+    #return '<'.$this->name.'>'.PHP_EOL;
+    return ''
+      .'<?xml version=“1.0” encoding=“utf-8”?>'.PHP_EOL
+      .'<semantico src="sem_ui" table="'.$this->name.'">'.PHP_EOL
+    ;
+  }
+
+  function rows_rec_xml($row) {
+    echo "\t<".$this->name.">".PHP_EOL;
+    foreach (array_keys($this->fields()) as $f) {
+      if ($row[$f] !== '') echo ''
+        . "\t\t<".$f.'>'
+        .'<![CDATA['.$row[$f].']]>'
+        . '</'.$f.'>'
+      .PHP_EOL;
+    }
+    echo "\t</".$this->name.">".PHP_EOL;
+  }
+
+  function rows_end_xml() {
+    #return '</'.$this->name.'>'.PHP_EOL;
+    return '</semantico>'.PHP_EOL;
+  }
+
+  /*-----------------------------------------------------------------
+    Csv
+  -----------------------------------------------------------------*/
+  function rows_begin_csv() {
+    echo join("\t",array_keys($this->fields()))."\n";
+  }
+
+  function rows_rec_csv($row) {
+    echo join("\t",array_values($row))."\n";
+  }
+
+  function rows_end_csv() {
+  }
+
+  /*-----------------------------------------------------------------
+    Html Table
+  -----------------------------------------------------------------*/
+// NB 14.04.14   function rows_begin_table($opt=array()) {
+  function rows_begin_table() {
+
+    $html = '<table class="'.$this->name.' rows border">'.PHP_EOL;
+
+    $html .= '<tr class="'.$this->name.' row bold">';
+    foreach (array_keys($this->fields()) as $f) {
+      $html .= '<th class="'.$f.'">'.$this->url_sort($f).'</th>';
+    }
+
+    $html .= "</tr>".PHP_EOL;
+    return $html;
+  }
+
+  function rows_rec_table($row) {
+    echo '<tr>';
+
+    foreach ($row as $k => $v) {
+      echo '<td class="'.$k.'">'.$v.'</td>';
+    }
+
+    echo '</tr>'.PHP_EOL;
+  }
+
+  function rows_end_table() {
+    return "</table>".PHP_EOL;
+  }
+
+  /*-----------------------------------------------------------------
+    Html Div
+  -----------------------------------------------------------------*/
+  function rows_begin_div() {
+    return '<div class="'.$this->name.' rows">'.PHP_EOL;
+  }
+
+  function rows_rec_div($row) {
+    echo '<ul class="border rad bgcolor">';
+
+    foreach ($row as $k => $v) {
+      echo '<li>'
+        .'<span class="key">'.$k.'</span>'
+        .': '
+        .'<span class="value">'.$v.'</span>'
+        .'</li>';
+    }
+
+    echo '</ul>'.PHP_EOL;
+  }
+
+  function rows_end_div() {
+    return "</div>".PHP_EOL;
+  }
+
+}
+?>
diff --git a/lib/php/default.php b/lib/php/default.php
new file mode 100644 (file)
index 0000000..0bcb745
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+ini_set('include_path', realpath(dirname(__FILE__)).':'.ini_get('include_path'));
+@define('DEBUG',(int)$_REQUEST['debug']);
+require_once((dirname(__FILE__).'/page.php'));
+
+
+function file_write($file,$data,$mode='w') {
+
+  if (!$ftmp = fopen($file,$mode)) {
+    if (DEBUG) warn("file_write(): Cant open $file in mode $mode");
+    return;
+  }
+
+  if (fwrite($ftmp,$data) === FALSE) {
+    if (DEBUG) warn("file_write(): Cant write $file in mode $mode");
+    return;
+  }
+
+  fclose($ftmp);
+  return 1;
+
+}
+
+function ls_dir($path,$recurse=false,$exp='') {
+
+  $ls = array();
+  $rep = opendir($path);
+  #die(">>>".$exp);
+
+  while($file = readdir($rep)) {
+
+    if ($file == '.' or $file == '..') continue;
+    
+    if (!$exp or preg_match('#\.('.$exp.')$#',$file)) {
+      $ls[] = $file;
+    }
+
+    if ($recurse and is_dir("$path/$file")) {
+#print ">>>$path/$file\n";
+
+      foreach (ls_dir("$path/$file",$recurse,$exp) as $l) {
+#print ">>>$path/$file << $l\n";
+        $ls[] = "$file/$l";
+      }
+
+    }
+
+    #echo "<div>$file</div>\n";
+
+  }
+
+  closedir($rep);
+
+#print ">>>$path: ".print_r($ls,true)."\n";
+  return $ls;
+
+}
+
+function ar_map($return,$array,$as_hash=false) {
+  $map = array_map(create_function('$a',"return($return);"),$array);
+  if ($as_hash) {
+    $new = array();
+    foreach ($map as $k => $v) {
+      foreach ($v as $kk => $vv) {
+        $new[$kk] = $vv;
+      }
+    }
+    $map = $new;
+  }
+  return $map;
+}
+
+?>
diff --git a/lib/php/gpio b/lib/php/gpio
new file mode 160000 (submodule)
index 0000000..b001a47
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit b001a47fc82003dc92a96ed7900f65b0eefe8eac
diff --git a/lib/php/page.php b/lib/php/page.php
new file mode 100644 (file)
index 0000000..74e5feb
--- /dev/null
@@ -0,0 +1,283 @@
+<?php
+if (
+  !@$_SERVER['DOCUMENT_ROOT'] and (realpath($argv[0]) == __FILE__)
+) {
+$Page = new Page(array(
+  #'title' => 'Test',
+  'call' => array(
+    'begin',
+    array('out', 'Hello World !!!'),
+    #array('out', print_r($_SERVER,true)),
+    'end',
+  ),
+));
+}
+
+class Page {
+
+  var $call = array();
+  var $output = '';
+
+  var $charset = 'utf-8';
+  var $content_type = 'xhtml';
+  var $title = '';
+
+  var $css = array();
+  var $css_code = '';
+
+  var $js = array();
+  var $js_code = '';
+
+  /*
+    Create
+  */
+  function Page($opt = array()) {
+
+    //// opt
+    foreach ($opt as $k => $v) {
+      if ( ! array_key_exists($k,$this) ) die ("Page->new(): unknow param $k = $v");
+      $this->$k = is_array($v) ? $v : trim($v);
+    }
+    
+    // Defaults
+    if ( ! array_key_exists('title',$opt) )
+      #$this->title = $GLOBALS['argv'][0]
+      $this->title = $this->filename2title($_SERVER['SCRIPT_NAME']);
+      #$this->title = preg_replace('@^.*?([^/\.]+)[^/]*@','\1',$_SERVER['SCRIPT_NAME']);
+      #die( $this->title);
+    ;
+
+    //// Default
+    #print_r($this);
+
+    //// Call
+    foreach ($this->to_array($this->call) as $v) {
+
+      if ( is_array($v) ) {
+        $k = $v[0]; $v = $v[1];
+        $this->$k($v);
+
+      } else {
+        $this->$v();
+
+      }
+
+    }
+
+  }
+
+  /*
+    Funtions
+  */
+  function out($v) {
+    echo $v;
+  }
+
+  function to_array($v) {
+    return (is_array($v) ? $v : array($v));
+  }
+
+  function filename2title($str) {
+    $str = preg_replace('@^.*?([^/\.]+)(/index)?(\..*?)$@','\1',$_SERVER['SCRIPT_NAME']);
+    $str = preg_replace('@[\'_-]+$@',' ',$str);
+    return mb_strtoupper(mb_substr($str,0,1)).mb_strtolower(mb_substr($str,1,mb_strlen($str)));
+  }
+
+  function ar_map($return,$array) {
+    return array_map(create_function('$a',"return($return);"),$array);
+  }
+
+  function js_quote($str) {
+    return "'".str_replace("'","\'",$str)."'";
+  }
+
+  function ansi2html($v) {
+    $html = array(
+      sprintf('%c[1;',27) => sprintf('<bold>%c[0;',27),
+
+        'black'   => 30,
+          'red'     => 31,
+            'green'   => 32,
+              'yellow'    => 33,
+                'blue'    => 34,
+                  'magenta' => 35,
+                    'cyan'    => 36,
+                      'white'   => 37,
+                        'default' => 39,
+      sprintf('%c[0;30m',27) => '<span style="color:black">',
+      sprintf('%c[0;31m',27) => '<span style="color:red">',
+      sprintf('%c[0;32m',27) => '<span style="color:green">',
+      sprintf('%c[0;33m',27) => '<span style="color:#E0E000">', # yellow
+      sprintf('%c[0;34m',27) => '<span style="color:blue">',
+      sprintf('%c[0;35m',27) => '<span style="color:magenta">',
+      sprintf('%c[0;36m',27) => '<span style="color:cyan">',
+      sprintf('%c[0;37m',27) => '<span style="color:white">',
+      #sprintf('%c[0;33m',27) => '<span style="color:yellow">', # yellow
+      sprintf('%c[0m',27)    => '</span>',
+    );
+    return preg_replace('/<bold>(<span style=")/','\1font-weight:bold;',
+      str_replace(array_keys($html),array_values($html),Page::str2txt($v))
+    );
+  }
+
+  function str2txt($v) {
+    static $replace_flags = null;
+    static $charset = null;
+    if ($replace_flags === null) {
+
+      $charset = strtoupper($this->charset);
+      #$replace_flags = ENT_COMPAT | ENT_DISALLOWED;
+      $replace_flags = ENT_COMPAT;
+
+      if (preg_match('/xhml/',$this->content_type)) {
+        $replace_flags = $replace_flags | ENT_XHTML;
+      } elseif (preg_match('/xml/',$this->content_type)) {
+        $replace_flags = $replace_flags | ENT_XML1;
+      } else {
+        #$replace_flags = $replace_flags | ENT_HTML401;
+        $replace_flags = $replace_flags | ENT_HTML5;
+      }
+
+    }
+
+    return htmlspecialchars($v,$replace_flags,$charset);
+  }
+
+  function tag($content='',$tag,$attrs='') {
+    
+    if (!is_array($content)) $content = array($content);
+    $tags = array();
+    foreach ($content as $c) {
+      $tags[] = ''
+        . '<' . $tag
+          . ($attrs ? " $attrs" : "")
+        . '>'
+        . $c
+        . "</$tag>"
+      ;
+    }
+    return join(PHP_EOL,$tags);
+    return ''
+      . '<' . $tag
+        . ($attrs ? " $attrs" : "")
+      . '>'
+      . $content
+      . "</$tag>"
+    ;
+  }
+
+  function debug($debug) {
+
+    if (is_array($debug) or is_object($debug)) $debug = print_r($debug,true);
+    if (preg_match('/ml$/',$this->content_type)) {
+      #echo "<!-- $debug -->";
+      echo "<pre class=\"ui-state-highlight debug\">".htmlentities($debug)."</pre>".PHP_EOL;
+
+    } else {
+      echo "DEBUG: $debug\n";
+
+    }
+
+  }
+
+  /*
+    Output
+  */
+  function begin() {
+
+    $this->headers(); 
+    echo $this->doctype(); 
+
+    if (preg_match('/html$/',$this->content_type)) {
+      echo '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">'.PHP_EOL;
+    }
+
+    echo $this->head(); 
+
+    if (preg_match('/ml$/',$this->content_type)) {
+
+      echo '<body>' . PHP_EOL;
+      if ($this->title) echo $this->tag($this->title,'h1').PHP_EOL;
+    }
+
+  }
+
+  function end() {
+
+    if (preg_match('/ml$/',$this->content_type)) {
+      echo '</body>' . PHP_EOL;
+      echo '</html>' . PHP_EOL;
+    }
+
+  }
+
+  function headers() {
+
+    header('Content-type: ' . $this->content_type);
+
+    return true;
+  }
+
+  function doctype($content_type = null) {
+    if (!$content_type) $content_type = $this->content_type;
+    $doctype = '';
+
+    if (preg_match('/frame/',$content_type)) {
+      $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'.PHP_EOL;
+    } else if (preg_match('/xhtml/',$content_type)) {
+      $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'.PHP_EOL;
+    } else {
+      $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'.PHP_EOL;
+    }
+
+    return $doctype;
+  }
+
+  function head() {
+    $head = '';
+
+    if (preg_match('/ml$/',$this->content_type)) {
+      $head .= '<head>'.PHP_EOL;
+      #$head .= '</head>'.PHP_EOL; return $head;
+
+      if ($this->title) $head .= $this->tag($this->title,'title') . PHP_EOL;
+      if ($this->charset) $head .= '<meta http-equiv="Content-Type" content="text/html; charset='.$this->charset.'" />'.PHP_EOL;
+      if (preg_match('/tml$/',$this->content_type)) {
+
+        foreach ($this->to_array($this->css) as $v) {
+          $head .= '<link rel="stylesheet" href="' . $v . '" />'.PHP_EOL;
+        }
+
+        if ($this->css_code) $head .=  ''
+          . '<style type="text/css"><!-- ' . PHP_EOL
+          . $this->css_code
+          . ' --></style>' . PHP_EOL
+        ;
+
+        foreach ($this->to_array($this->js) as $v) {
+          $head .= '<script src="' . $v . '" type="text/javascript"></script>' . PHP_EOL;
+        }
+        if ($this->js_code) $head .=  ''
+          . '<script type="text/javascript"><!-- ' . PHP_EOL
+          . trim($this->js_code)."\n"
+          . ' --></script>' . PHP_EOL
+        ;
+
+
+      }
+
+      $head .= '</head>'.PHP_EOL;
+    }
+
+    return $head;
+  }
+
+  function is($is) {
+    if ($is == 'xhtml') return preg_match('/xhtml$/',$this->content_type);
+    if ($is == 'html') return preg_match('/html$/',$this->content_type);
+    if ($is == 'xml') return preg_match('/xml$/',$this->content_type);
+    die("Db->is(): unknow value '$is'");
+  }
+}
+return;
+?>