Skip to content

Instantly share code, notes, and snippets.

@brzuchal
Last active July 6, 2020 13:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brzuchal/7d17cab0c7c51c269316c22cd202d346 to your computer and use it in GitHub Desktop.
Save brzuchal/7d17cab0c7c51c269316c22cd202d346 to your computer and use it in GitHub Desktop.
PHP Marco Candidates

PHP Macro

Call syntax

Macro call syntax in ABNF:

arg-name   = 1*ALPHA
arg        = [ arg-name ":" ] ( expr | statement )
arg-list   = [ arg [ "," ] ]
arg-list  /= arg "," arg-list
macro-name = 1*ALPHA
macro-call = name "!" [ "(" arg-list ")" ]

Example:

foo!;
bar!();
baz!(true, limit 1);

Open questions

Importing macros

Importing user-defined macros differs from loading function declarations in PHP due to it's nature. How to import user-defined macros if they're compile time and include* family functions operate in runtime?

If I got it right: One solution is to compile all macro declarations on compilation time before processing the ast.

Macro declaration

First there are two kinds of macros. One which substitute to proper ast replaced in place of macro invocation and second which don't resolve cause they emit opcodes or influence compiler.

Below is an example of macro declaration. First the eval! actually is the unresolvable one but the second resolves into ast substitution in place of it's call.

macro!(assert, {
    ($assertion, $msg, $throw) => {
        if (!assert_enabled()) return;
        $msg = $msg ?? 'Default error: ' . stringify!($assertion);
        $throw = $throw ?? new!(AssertionException, %$msg);
        rerturn if!(
            %$assertion,
            throw!(%$throw)
        );
    }
});

Note! Old, macros should return ast nodes like above.

macro!(eval, {
    ($e:expr) => { eval((string) $e); }
});
macro!(foo, {
    ($f:expr) => { if ($f) echo "Foo!\n"; };
    ($($args:expr,*)* $l:literal) => {
        foreach ($args as $arg) {
            var_dump(eval!($arg));
        }
        echo "Literal: {$l}";
    };
});

Use of foo! macro:

foo!; // ERROR
foo!(true); // echo "Foo!\n"
foo!("Hello", upper('w') . 'orld', SOME_Lit3ral);

Candidates

Compiler directives

declare!(strict_types: 1);

Runtime assertions

assert!(false, throw: new FalseAssertionException());
assert!(false, msg: 'Assertion failed');

Runtime errors verbosity controll

$level = error_reporting!(level: E_ALL);
$error = error_get_last!;
error_clear_last!;

Execution context access

$vars = compact!('_ENV');
extract!($argv);
$vars = get_defined_vars!;

$empty = empty!($foo);
$isset = isset!($foo);
unset!($_ENV);

$argc = argc!;
$argv = argv!;
$arg = argv!('arg_name');

$trace = trace!(limit: 1); // debug_backtrace()
print_trace!(limit: 1); // debug_print_backtrace()

$callee = callee!; // equivalent of trace!(DEBUG_BACKTRACE_IGNORE_ARGS, limit: 1)[0];

dump!($foo); // debug_zval_dump()

Execution stopping

exit!
exit!(1);
die!;
die!("The end.\n");
set_time_limit!(60);

Triggering errors

trigger_error!("Cannot divide by zero", E_USER_ERROR);

Compiler stopping

halt!;

Runtime handlers

register!(autoload: 'myLoader', exception: 'exceptionHandler', error: 'errorHandler');
autoload_unregister!('myLoader');
autoload_call!();
restore_error_handler!();
restore_exception_handler!();

Include loading

restore_include_path!;
$path = get_include_path!;
$path = set_include_path!('/usr/lib/pear');

Runtime settings

ini_set!('memory_limit', '1G');

AST retrieving

ast!(2 + 2);

Controlling HTTP SAPI

header!('Content-Type', 'application/json');
$headers = headers_list!;
$headers = headers_sent!;

setcookie!('my_cookie', date('Y-m-d'));
setrawcookie!('my_cookie', date('Y-m-d'));
getcookie!('my_cookie'); // ??
$cookies = cookies_list!; // ??
$cookies = cookies_sent!; // ??
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment