#!/usr/bin/php -q
<?php
/*
   Makes small test scripts, which get extracted from the PHP manual
   on the fly (but not every function has one there, suddenly).
*/

#-- config
$PAUSE = 0;   // (use 3) break between tests; you should use `|less` instead
$CLEAN = 0;   // remove test scripts after use
$MANY = 1;    // create one testing script per function


#-- help
if (count($_SERVER["argv"]) < 3) {
   echo<<<END

Usage: doctest  [php-interpreter]  [/path/to/your/php/manual/]

   Runs the examples from your locally installed PHP manual (any language
   will work, but get the latest!) with the given PHP interpreter version
   (use an older version to really test the emulated functions). You need
   lynx or w3m installed also. Use only under U*ix/Linux.

   Pipe the output through |less, |more or |most, there are built-in
   delays. Rarely example scripts may not run (they are examples only).


END;
}

#-- proceed
else {


   #-- args
   $php = php_interpreter();
   $dir = get_manual();
   $html = get_lynx();
   $tmpdir = $tmp = tmp();


   #-- load emulation script
   chdir(".");
   system("./doc/devtools/test-up");
   # dirname(dirname(__FILE__)) );
   $upgrade_php = "upgrade.php";
   $test_up = "test-up.php";
   $emu2 = "ext/array.php";
   $emu3 = "ext/mime.php";
   require($upgrade_php);



   #-- get function names
   $funcs = emulated_functions();

   #-- generate list of really emulated functions
   $simulated = simulated();
   $dont = array("time_nanosleep");



   #-- all
   $funcs = emulated_functions();
   foreach ($funcs as $func) {

      # read from php-manual
      if (($fn = func2fn($func))
       and !in_array($func, $dont) )
#      and $ALL || in_array($func, $simulated) )
      {
         echo "checking function '\033[31m$func\033[37m'... ";


         #-- grep example scripts
         $tests = example_scripts($func);

         #-- exec each
         if ($n = count($tests)) {

            #-- multiple scripts to run
            foreach ($tests as $i=>$script) {

               add_output($script);   // add print or echo if missing

               up_version($script, $func);   // use test-up.php "up_functionname"

               #-- output sample script text from doc
               print_script($script);

               #-- create temp script, run it
               if ($MANY) {
                  $tmp = "$tmpdir/$func#$i.php";
               }
               $script = ltrim($script);
               file_put_contents(
                  $tmp,
                  "<?php\n\n".
                  "#-- a test script for emulated function '$func'\n".
#                  "if (function_exists('$func')) { echo \"ATTENTION: the native '$func' function will engage for this test\\n\"; }\n".
                  "include('$test_up');\n".
#                  "#include('$emu2');\n".
#                  "#include('$emu3');\n".
                  "error_reporting(E_ALL);\n\n".
                  "#-- example[$i] as taken from PHP manual page '$fn'\n".
                  "$script\n".
                  "\n?".">"
               );
               echo "==>\033[32m\n";
               passthru("$php $tmp 2>&1");
               echo "\033[0;37m\n\n";
            }

            #-- pause before next func/script
            sleep($PAUSE);
            echo "\n-------------------------------------------------------------------\n\n";
         }

         else {
            echo "NO EXAMPLES found in doc, skipping";
         }
         echo "\n";
      }
   }

   #-- clean up
   if ($CLEAN) {
      unlink($tmp);
   }
}

#-- even more clean
if ($MANY && $CLEAN) { @rmdir($tmpdir); }





   function emulated_functions($upgrade_php = "upgrade.php") {
      preg_match_all("/function[ ]+(?:up_)?([_\w\d]+)\s*\(/", file_get_contents($upgrade_php), $uu);
      return $uu[1];
   }


function php_interpreter() {
   $php = $_SERVER["argv"][1];
   $php = trim(`which $php`);
   if (!$php) {
      die(__FILE__.": Given PHP interpreter not in your %PATH!\n");
   }
   return $php;
}

function get_manual() {
   $dir = $_SERVER["argv"][2];
   if (!is_dir($dir) || !file_exists("$dir/function.print.html")) {
      die(__FILE__.": PHP manual does not live under '$dir'.\n");
   }
   return $dir;
}
function get_lynx() {
   ($html = `which w3m`) or ($html = `which lynx`);
   if (!($html = trim($html))) {
      die(__FILE__.": lynx or w3m required.\n");
   }
   return $html;
}
function tmp() {
   global $MANY;
   $tmp = "/tmp/upgrade.php.doctest.tmp";
   $tmpdir = $tmp;
   if ($MANY) { @unlink($tmpdir); @mkdir($tmpdir); }
   return $tmp;
}

   #-- generate list of really emulated functions
function simulated() {
   global $php;
   $simulated = `echo '<?php echo serialize(get_defined_functions()); ?>' | $php -q`;
   $simulated = unserialize($simulated);
   return $simulated["internal"];
   
}


function func2fn($func) {
   global $dir;
  $fn = "$dir/function.".strtr($func, "_", "-").".html";
  if (file_exists($fn)) { return $fn; }
}


function example_scripts($func) {
   global $html;
   $fn = func2fn($func);
   $text = `$html -dump $fn`;
   preg_match_all("/<\?php(.+?)\?".">/ms", $text, $uu);
   return($uu[1]);
}


function add_output(&$scr) {
               #-- fix output-less scripts: find last assigned-to variable name
               if (!preg_match("/echo|print|var_dump/", $scr)) {
                  if (preg_match('/^.+(\$[_\w\d\[\"\'\]]+)\s*=/s', $scr, $uu)) {
                     $scr .= "\n\n#-- auto-added\nprint_r($uu[1]);\n";
                  }
                  else {
                     $scr .= "\n\n#-- this script gives no useful output, or does it?";
                  }
               }
               #-- fix compatibility to older PHP versions
               $scr = preg_replace('/(\s)private(\s\$)/', '$1var$2', $scr);
}
function up_version(&$scr, $func) {
   $scr = preg_replace("/$func/", "up_$func", $scr);
}

function print_script($scr) {
   global $i, $PAUSE;
               #-- output sample script text from doc
               if ($i) {
                  echo "\n++++++++++++++++++++++++++++\n\n";
                  sleep($PAUSE);
               }
               echo "\033[1;30m<?php $scr\n?".">\033[0;37m\n";
}

?>