Skip to content

Commit 63d4f31

Browse files
committed
- Added -B, -F, -R, -E for line by line std in processing.
- Added some error messages. - Added more -h info. @added command line parameters -B, -F, -R and -E which allow to process @stdin line by line (See php -h for more). (marcus) #This allows to simply complex command line constructs...
1 parent c4528f8 commit 63d4f31

File tree

1 file changed

+219
-45
lines changed

1 file changed

+219
-45
lines changed

sapi/cli/php_cli.c

Lines changed: 219 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,18 @@
7676

7777
#include "php_getopt.h"
7878

79-
#define PHP_MODE_STANDARD 1
80-
#define PHP_MODE_HIGHLIGHT 2
81-
#define PHP_MODE_INDENT 3
82-
#define PHP_MODE_LINT 4
83-
#define PHP_MODE_STRIP 5
84-
#define PHP_MODE_CLI_DIRECT 6
79+
#define PHP_MODE_STANDARD 1
80+
#define PHP_MODE_HIGHLIGHT 2
81+
#define PHP_MODE_INDENT 3
82+
#define PHP_MODE_LINT 4
83+
#define PHP_MODE_STRIP 5
84+
#define PHP_MODE_CLI_DIRECT 6
85+
#define PHP_MODE_PROCESS_STDIN 7
8586

8687
extern char *ap_php_optarg;
8788
extern int ap_php_optind;
8889

89-
#define OPTSTRING "aCc:d:ef:g:hilmnqr:sw?vz:"
90+
#define OPTSTRING "aB:Cc:d:E:eF:f:g:hilmnqR:r:sw?vz:"
9091

9192
static int print_module_info(zend_module_entry *module, void *arg TSRMLS_DC)
9293
{
@@ -295,9 +296,12 @@ static void php_cli_usage(char *argv0)
295296
prog = "php";
296297
}
297298

298-
php_printf( "Usage: %s [options] [-f] <file> [args...]\n"
299-
" %s [options] -r <code> [args...]\n"
300-
" %s [options] [-- args...]\n"
299+
php_printf( "Usage: %s [options] [-f] <file> [--] [args...]\n"
300+
" %s [options] -r <code> [--] [args...]\n"
301+
" %s [options] [-B <begin_code>] -R <code> [-E <end_code>] [--] [args...]\n"
302+
" %s [options] [-B <begin_code>] -F <file> [-E <end_code>] [--] [args...]\n"
303+
" %s [options] [--] [args...]\n"
304+
"\n"
301305
" -a Run interactively\n"
302306
" -c <path>|<file> Look for php.ini file in this directory\n"
303307
" -n No php.ini file will be used\n"
@@ -309,14 +313,38 @@ static void php_cli_usage(char *argv0)
309313
" -l Syntax check only (lint)\n"
310314
" -m Show compiled in modules\n"
311315
" -r <code> Run PHP <code> without using script tags <?..?>\n"
316+
" -B <begin_code> Run PHP <begin_code> before processing input lines\n"
317+
" -R <code> Run PHP <code> for every input line\n"
318+
" -F <file> Parse and execute <file> for every input line\n"
319+
" -E <end_code> Run PHP <end_code> after processing all input lines\n"
312320
" -s Display colour syntax highlighted source.\n"
313321
" -v Version number\n"
314322
" -w Display source with stripped comments and whitespace.\n"
315323
" -z <file> Load Zend extension <file>.\n"
316324
"\n"
317-
" args... Arguments passed to script. Use -- args when first argument \n"
325+
" args... Arguments passed to script. Use -- args when first argument\n"
318326
" starts with - or script is read from stdin\n"
319-
, prog, prog, prog);
327+
"\n"
328+
"The PHP Command Line Interface 'CLI' supports the following operation modes:\n"
329+
"\n"
330+
" You can parse and execute files by using parameter -f followed by the\n"
331+
" name of the file to be executed.\n"
332+
"\n"
333+
" Using parameter -r you can directly execute PHP code simply as you would\n"
334+
" do inside a php file when using the eval() function.\n"
335+
"\n"
336+
" It is also possible to process the standard input line by line using either\n"
337+
" the parameter -R or -F. In this mode each separate input line causes the\n"
338+
" code specified by -R (see -r) or the file specified by -F to be executed.\n"
339+
" You can access the input line by $argn. While processing the input lines\n"
340+
" $argi contains the number of the actual line being processed. Further more\n"
341+
" the paramters -B and -E can be used to execute code (see -r) before and\n"
342+
" after input line processing respectively.\n"
343+
"\n"
344+
" If none of -r -f -B -R -F and -E is present but a single parameter is given\n"
345+
" then this parameter is taken as the filename to process (same as with -f)\n"
346+
" If no parameter is present then the standard input is read and executed.\n"
347+
, prog, prog, prog, prog, prog);
320348
}
321349
/* }}} */
322350

@@ -397,6 +425,44 @@ static void cli_register_file_handles(TSRMLS_D)
397425
FREE_ZVAL(zerr);
398426
}
399427

428+
static const char *param_mode_conflict = "Either execute direct code, process stdin or use a file.\n";
429+
430+
/* {{{ cli_seek_file_begin
431+
*/
432+
static int cli_seek_file_begin(zend_file_handle *file_handle, char *script_file, int *lineno)
433+
{
434+
int c;
435+
436+
*lineno = 1;
437+
438+
if (!(file_handle->handle.fp = VCWD_FOPEN(script_file, "rb"))) {
439+
SG(headers_sent) = 1;
440+
SG(request_info).no_headers = 1;
441+
php_printf("Could not open input file: %s.\n", script_file);
442+
return FAILURE;
443+
}
444+
file_handle->filename = script_file;
445+
/* #!php support */
446+
c = fgetc(file_handle->handle.fp);
447+
if (c == '#') {
448+
while (c != 10 && c != 13) {
449+
c = fgetc(file_handle->handle.fp); /* skip to end of line */
450+
}
451+
/* handle situations where line is terminated by \r\n */
452+
if (c == 13) {
453+
if (fgetc(file_handle->handle.fp) != 10) {
454+
long pos = ftell(file_handle->handle.fp);
455+
fseek(file_handle->handle.fp, pos - 1, SEEK_SET);
456+
}
457+
}
458+
*lineno = 2;
459+
} else {
460+
rewind(file_handle->handle.fp);
461+
}
462+
return SUCCESS;
463+
}
464+
/* }} */
465+
400466
/* {{{ main
401467
*/
402468
int main(int argc, char *argv[])
@@ -415,8 +481,9 @@ int main(int argc, char *argv[])
415481
int interactive=0;
416482
int module_started = 0;
417483
int lineno = 0;
418-
char *exec_direct=NULL;
419-
char *param_error=NULL;
484+
char *exec_direct=NULL, *exec_run=NULL, *exec_begin=NULL, *exec_end=NULL;
485+
const char *param_error=NULL;
486+
int scan_input = 0;
420487
/* end of temporary locals */
421488
#ifdef ZTS
422489
zend_compiler_globals *compiler_globals;
@@ -542,9 +609,27 @@ int main(int argc, char *argv[])
542609
CG(extended_info) = 1;
543610
break;
544611

612+
case 'F':
613+
if (behavior == PHP_MODE_PROCESS_STDIN) {
614+
if (exec_run || script_file) {
615+
param_error = "You can use -R or -F only once.\n";
616+
break;
617+
}
618+
} else if (behavior != PHP_MODE_STANDARD) {
619+
param_error = param_mode_conflict;
620+
break;
621+
}
622+
behavior=PHP_MODE_PROCESS_STDIN;
623+
script_file = ap_php_optarg;
624+
no_headers = 1;
625+
break;
626+
545627
case 'f': /* parse file */
546-
if (behavior == PHP_MODE_CLI_DIRECT) {
547-
param_error = "Either execute direct code or use a file.\n";
628+
if (behavior == PHP_MODE_CLI_DIRECT || behavior == PHP_MODE_PROCESS_STDIN) {
629+
param_error = param_mode_conflict;
630+
break;
631+
} else if (script_file) {
632+
param_error = "You can use -f only once.\n";
548633
break;
549634
}
550635
script_file = ap_php_optarg;
@@ -606,7 +691,7 @@ int main(int argc, char *argv[])
606691

607692
#if 0 /* not yet operational, see also below ... */
608693
case '': /* generate indented source mode*/
609-
if (behavior == PHP_MODE_CLI_DIRECT) {
694+
if (behavior == PHP_MODE_CLI_DIRECT || behavior == PHP_MODE_PROCESS_STDIN) {
610695
param_error = "Source indenting only works for files.\n";
611696
break;
612697
}
@@ -619,16 +704,64 @@ int main(int argc, char *argv[])
619704
break;
620705

621706
case 'r': /* run code from command line */
622-
if (behavior != PHP_MODE_STANDARD) {
623-
param_error = "Either execute direct code or use a file.\n";
707+
if (behavior == PHP_MODE_CLI_DIRECT) {
708+
if (exec_direct || script_file) {
709+
param_error = "You can use -r only once.\n";
710+
break;
711+
}
712+
} else if (behavior != PHP_MODE_STANDARD) {
713+
param_error = param_mode_conflict;
624714
break;
625715
}
626716
behavior=PHP_MODE_CLI_DIRECT;
627717
exec_direct=ap_php_optarg;
628718
break;
719+
720+
case 'R':
721+
if (behavior == PHP_MODE_PROCESS_STDIN) {
722+
if (exec_run || script_file) {
723+
param_error = "You can use -R or -F only once.\n";
724+
break;
725+
}
726+
} else if (behavior != PHP_MODE_STANDARD) {
727+
param_error = param_mode_conflict;
728+
break;
729+
}
730+
behavior=PHP_MODE_PROCESS_STDIN;
731+
exec_run=ap_php_optarg;
732+
break;
733+
734+
case 'B':
735+
if (behavior == PHP_MODE_PROCESS_STDIN) {
736+
if (exec_begin) {
737+
param_error = "You can use -B only once.\n";
738+
break;
739+
}
740+
} else if (behavior != PHP_MODE_STANDARD) {
741+
param_error = param_mode_conflict;
742+
break;
743+
}
744+
behavior=PHP_MODE_PROCESS_STDIN;
745+
exec_begin=ap_php_optarg;
746+
break;
747+
748+
case 'E':
749+
if (behavior == PHP_MODE_PROCESS_STDIN) {
750+
if (exec_end) {
751+
param_error = "You can use -E only once.\n";
752+
break;
753+
}
754+
} else if (behavior != PHP_MODE_STANDARD) {
755+
param_error = param_mode_conflict;
756+
break;
757+
}
758+
scan_input = 1;
759+
behavior=PHP_MODE_PROCESS_STDIN;
760+
exec_end=ap_php_optarg;
761+
break;
629762

630763
case 's': /* generate highlighted HTML from source */
631-
if (behavior == PHP_MODE_CLI_DIRECT) {
764+
if (behavior == PHP_MODE_CLI_DIRECT || behavior == PHP_MODE_PROCESS_STDIN) {
632765
param_error = "Source highlighting only works for files.\n";
633766
break;
634767
}
@@ -650,7 +783,7 @@ int main(int argc, char *argv[])
650783
break;
651784

652785
case 'w':
653-
if (behavior == PHP_MODE_CLI_DIRECT) {
786+
if (behavior == PHP_MODE_CLI_DIRECT || behavior == PHP_MODE_PROCESS_STDIN) {
654787
param_error = "Source stripping only works for files.\n";
655788
break;
656789
}
@@ -676,38 +809,26 @@ int main(int argc, char *argv[])
676809
CG(interactive) = interactive;
677810

678811
/* only set script_file if not set already and not in direct mode and not at end of parameter list */
679-
if (argc > ap_php_optind && !script_file && behavior!=PHP_MODE_CLI_DIRECT && strcmp(argv[ap_php_optind-1],"--")) {
812+
if (argc > ap_php_optind
813+
&& !script_file
814+
&& behavior!=PHP_MODE_CLI_DIRECT
815+
&& behavior!=PHP_MODE_PROCESS_STDIN
816+
&& strcmp(argv[ap_php_optind-1],"--"))
817+
{
680818
no_headers = 1;
681819
script_file=argv[ap_php_optind];
682820
ap_php_optind++;
683821
}
684822
if (script_file) {
685-
if (!(file_handle.handle.fp = VCWD_FOPEN(script_file, "rb"))) {
686-
SG(headers_sent) = 1;
687-
SG(request_info).no_headers = 1;
688-
php_printf("Could not open input file: %s.\n", script_file);
823+
if (cli_seek_file_begin(&file_handle, script_file, &lineno) != SUCCESS) {
689824
goto err;
690825
}
691-
file_handle.filename = script_file;
692826
script_filename = script_file;
693-
/* #!php support */
694-
c = fgetc(file_handle.handle.fp);
695-
if (c == '#') {
696-
while (c != 10 && c != 13) {
697-
c = fgetc(file_handle.handle.fp); /* skip to end of line */
698-
}
699-
/* handle situations where line is terminated by \r\n */
700-
if (c == 13) {
701-
if (fgetc(file_handle.handle.fp) != 10) {
702-
long pos = ftell(file_handle.handle.fp);
703-
fseek(file_handle.handle.fp, pos - 1, SEEK_SET);
704-
}
705-
}
706-
lineno = 2;
707-
} else {
708-
rewind(file_handle.handle.fp);
709-
}
710827
} else {
828+
/* We could handle PHP_MODE_PROCESS_STDIN in a different manner */
829+
/* here but this would make things only more complicated. And it */
830+
/* is consitent with the way -R works where the stdin file handle*/
831+
/* is also accessible. */
711832
file_handle.filename = "-";
712833
file_handle.handle.fp = stdin;
713834
}
@@ -716,7 +837,7 @@ int main(int argc, char *argv[])
716837
file_handle.free_filename = 0;
717838
php_self = file_handle.filename;
718839

719-
/* before registering argv to modulule exchange the *new* argv[0] */
840+
/* before registering argv to module exchange the *new* argv[0] */
720841
/* we can achieve this without allocating more memory */
721842
SG(request_info).argc=argc-ap_php_optind+1;
722843
arg_excp = argv+ap_php_optind-1;
@@ -796,6 +917,59 @@ int main(int argc, char *argv[])
796917
exit_status=254;
797918
}
798919
break;
920+
921+
case PHP_MODE_PROCESS_STDIN:
922+
{
923+
char input[4096];
924+
size_t len, index = 0;
925+
php_stream_context *sc_in = php_stream_context_alloc();
926+
php_stream *s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in);
927+
pval *argn, *argi;
928+
929+
if (exec_begin && zend_eval_string(exec_begin, NULL, "Command line begin code" TSRMLS_CC) == FAILURE) {
930+
exit_status=254;
931+
}
932+
ALLOC_ZVAL(argi);
933+
Z_TYPE_P(argi) = IS_LONG;
934+
Z_LVAL_P(argi) = index;
935+
INIT_PZVAL(argi);
936+
zend_hash_update(&EG(symbol_table), "argi", sizeof("argi"), &argi, sizeof(pval *), NULL);
937+
while (exit_status == SUCCESS && php_stream_gets(s_in, input, sizeof(input))) {
938+
len = strlen(input);
939+
while (len-- && (input[len]=='\n' || input[len]=='\r')) {
940+
input[len] = '\0';
941+
}
942+
ALLOC_ZVAL(argn);
943+
Z_TYPE_P(argn) = IS_STRING;
944+
Z_STRLEN_P(argn) = ++len;
945+
Z_STRVAL_P(argn) = estrndup(input, len);
946+
INIT_PZVAL(argn);
947+
zend_hash_update(&EG(symbol_table), "argn", sizeof("argn"), &argn, sizeof(pval *), NULL);
948+
Z_LVAL_P(argi) = ++index;
949+
if (exec_run) {
950+
if (zend_eval_string(exec_run, NULL, "Command line run code" TSRMLS_CC) == FAILURE) {
951+
exit_status=254;
952+
}
953+
} else {
954+
if (script_file) {
955+
if (cli_seek_file_begin(&file_handle, script_file, &lineno) != SUCCESS) {
956+
exit_status = 1;
957+
} else {
958+
CG(start_lineno) = lineno;
959+
php_execute_script(&file_handle TSRMLS_CC);
960+
exit_status = EG(exit_status);
961+
}
962+
}
963+
}
964+
}
965+
if (exec_end && zend_eval_string(exec_end, NULL, "Command line end code" TSRMLS_CC) == FAILURE) {
966+
exit_status=254;
967+
}
968+
969+
php_stream_close(s_in);
970+
php_stream_context_free(sc_in);
971+
break;
972+
}
799973
}
800974

801975
if (cli_sapi_module.php_ini_path_override) {

0 commit comments

Comments
 (0)