#!/usr/bin/php -qC
<?php
/**
 * title: Shell PHP functions
 * description: Pipe from and to PHP functions from the shell.
 * type: cli
 * version: 0.8
 * depends: php:cli
 *
 * Wrapper script which allows PHP functions to be called directly from the
 * shell. Parameters can be supplied directly as arguments, or piped in via
 * STDIN - which either becomes the first parameter, or substitutes any
 * "-" argument placeholder.
 *
 *    strtolower ABCDE
 *    echo "x?? 2" | urlencode
 *    echo "p" | strpos "inpos" -
 *
 * The simple invocation forms are Most useful with string functions.
 * But arrays can be passed in, and results are returned as JSON.
 *
 *    echo [1,2,3] | array_push - 4 | print_r
 *
 * Mostly function return values are output, but for &$result signatures
 * the reference-parameter will be assumed as primary result value.
 *
 *    preg_match /A+/ AAABBBCCC
 *
 * Setup requires just a few symlinks
 * e.g.
 *       ln -s  PhpFunctionCall strtolower
 *       ln -s  PhpFunctionCall base64_decode
 *       ln -s  PhpFunctionCall unserialize
 *
 * The included Makefile will create the most useful shortcuts.
 *
 */


   #-- Function name
   $func = basename(array_shift($_SERVER["argv"]));
   set_error_handler("stderr_color");

   #-- Arguments used as function params
   $args = $_SERVER["argv"];
   $dashes = count(array_intersect($args, ["-"]));

   #-- STDIN can be input parameter(s)
   if (!posix_isatty(STDIN) and is_string($stdin = rtrim(fread(STDIN, 1<<24), "\n"))
   or ($dashes and is_null($stdin = NULL)))
   {
       // Unpack JSON
       if (preg_match("/^ \s* [\[\{] .+ [\]\}] \s* $/smix", $stdin)) {
           $stdin = json_decode($stdin, TRUE);
       }

       // Allot array values to multiple params
       if (is_array($stdin) and ($dashes >= 2)) {
           while (count($stdin)) {
               $args[array_search("-", $args, TRUE)] = array_shift($stdin);
           }
       }
       // Substitute `-` args
       elseif ($dashes == 1) {
           $args[array_search("-", $args, TRUE)] = $stdin;
       }
       // Just use as first param
       else {
           $args = array_merge([$stdin], $args);
       }
   }
#print_r($args);


   #-- Run target function, capture result value
   list($fref, $i_result) = reflect_args($func, $args);
#$func =$fref;
   $r = $fref->invokeArgs($args);
   if (is_int($i_result) && is_scalar($r) && !is_string($r)) {
      $r = $args[$i_result];
   }
var_dump($func, $args, $r);

   #-- Output result
   if (in_array($func, str_getcsv("print_r,print,var_dump")) && !is_string($r)) {
       // as errno
       exit(intval($r));
   }
   else {
       // string or JSON struct
       print (is_scalar($r) || is_null($r))
           ? strval($r)
           : json_encode($r, JSON_PRETTY_PRINT);
   }


// print PHP errors/warnings to STDERR
function stderr_color($errno, $errstr, $errfile=NULL, $errline=NULL, $errcontext=NULL) {
   $errname = array_search($errno, get_defined_constants());
   global $func;
   fwrite(STDERR, "\x1b[31m$errname \x1b[0m(\x1b[32m$func\x1b[0m): \x1b[33m$errstr\x1b[0m\n");
}


// Apply & references, if one of the function parameters expects one, keep that as result parameter index
function reflect_args($func, & $args, $i_result=NULL) {
   $fref = new ReflectionFunction($func);
   foreach ($fref->getParameters() as $i => $param) {
       if ($i < count($args)) {
           $args[$i] = & $args[$i];
           if ($param->isPassedByReference()) {
               $i_result = $i;
           }
       }
   }
   return [$fref, $i_result];
}


?>