I've found this piece of nifty PHP code somewhere online:
<?=($__='`#`#'^'*/).')(($_='->.<:'^'__#[_')('>'^#_,'%'^#_)),$__($_('|'^'=','|'^'&')),$__($_(':'^"\n",';'^']'^#_));
When you run it, it produces the following string of text:
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
Why does this work the way it does?
What you start out with is <?=(value),(value),(value); so you can rewrite it like this.
echo ($__='`#`#'^'*/).')(($_='->.<:'^'__#[_')('>'^#_,'%'^#_));
echo $__($_('|'^'=','|'^'&'));
echo $__($_(':'^"\n",';'^']'^#_));
Line 1:
$__ = '`#`#'^'*/).' uses ^, which is the bitwise operator to shift `#`# by */). and set $__ to the string 'JoIn', next it does this to $_, setting it to the string 'range'.
$__() and $_() are now aliases of join() and range().
Making the first line easier to read:
echo ($__='join')(($_='range')('>'^#_,'%'^#_));
Wrapping pieces in parenthesis serves to set the var and then pass its value as an expression so you can do fancy things like this.
A little cleaner:
$__ = 'join';
$_ = 'range';
echo $__($_('>'^#_, '%'^#_));
The two args for the inner function: '>'^#_ and '%'^#_, are 'a' and 'z'. (These abuse the error suppression operator # to kill a warning and use _ as a string, so '>'^'_' and '%'^'_')
Now you have echo join(range('a', 'z')); giving you abcdefghijklmnopqrstuvwxyz.
Second line passes '|'^'=' and '|'^'&' or A and Z to those two functions.
And the third line passes 0 and 9.
The biggest takeaway from this is that you should never use any of these tricks in production code.
Related
I am trying to pass a string from PHP into a Perl script, uppercase each word, and then pass the uppercase string back to PHP where I echo it out. I know I can do that in PHP, but there are other reasons why I will need to pass the string between PHP and Perl.
If the variable $user_input is "first second third" I want the echo statement in the PHP program to echo "FIRST SECOND THIRD", but right now it is only echoing "FIRST".
How do I get my Perl script to read in and uppercase each word, not just the first one? (The variable user_input is not necessarily fixed at a length of three words, it could be more or fewer.)
Here is the relevant PHP:
$result = shell_exec('/usr/bin/perl /var/www/my_site/myperl.pl ' . $user_input);
$user_input = $result;
echo $user_input;
And this is my Perl program (myperl.pl):
#!/usr/bin/perl
use warnings;
my $var1 = shift;
foreach $var ($var1){
print uc $var ;
}
I've tried changing several things in the Perl code (using #var1 for example) but can't seem to get it to work. Thanks in advance for any help you can provide.
Do not use shell_exec() with unescaped input. This function is really easy to use in an unsafe manner, and can lead to command injection security vulnerabilities. Here, you have run into issues around your lack of escaping. Slightly simplifying things, you are trying to execute the following command:
# shell_exec('/usr/bin/perl /var/www/my_site/myperl.pl ' . $user_input)
/usr/bin/perl /var/www/my_site/myperl.pl first second third
Since the variable contents are just pasted into the command line, it is split at spaces and passed to the command as separate arguments.
The safest approach is to bypass shell expansion and executing the intended program directly. Unfortunately, core PHP does not provide convenient functions for this task.
If you want to continue using the convenient shell_exec() function, you MUST escape the variable before constructing the shell command. For example:
shell_exec('/usr/bin/perl /var/www/my_site/myperl.pl ' . escapeshellarg($user_input))
This would lead to the following shell command being executed (note the single quotes around the argument):
/usr/bin/perl /var/www/my_site/myperl.pl 'first second third'
After that, your Perl code should work as expected.
Arguments passed to a Perl program are available in the builtin global #ARGV array.
The shift function takes an array name as an argument, and removes the first element from that array and returns it. When used without an argument it operates on #ARGV, or if in a subroutine on #_ array which holds arguments passed to the subroutine.
So the code you show takes the first argument passed to the program and works with it.† Then, the shown foreach loop goes over just that one scalar (single-valued) variable.
Instead, you want to extract or copy the whole #ARGV and then to iterate over that array ‡
use warnings;
use strict;
use feature qw(say);
my #args = #ARGV;
foreach my $arg (#args) {
say uc $arg; # with a new line
# print uc $arg; # no newline, all come out "stuck" together
}
Now you can process those (instead of just uc-ing them), what I presume is intended.
If you indeed wanted to merely print out upper-cased input then
say uc for #ARGV; # each on a line on its own
# print uc for #ARGV; # no newline between them
suffices. (The uc takes $_ by default.)
† If the PHP program passes one string to the Perl program, not expanded (broken into) words by a shell or some such, then your Perl code should work as it stands. But the code shown here works in that case as well, and it will always process all arguments.
‡ Or of course one can work with the #ARGV directly
foreach my $arg (#ARGV) { ... }
I copy it in the text to preserve it because normally it is parsed separately by libraries.
This is genuine question (but also code golf for PHP devs).
I know array_key_last() exists in PHP.
I would like to use array_value_last() on explode('/', __FILE__) but... array_value_last() doesn't exist.
I am working inside a constrained environment (an included file which contains and runs a function, before returning data) and I have tried both:
array_pop(explode('/', __FILE__))
end(explode('/', __FILE__))
and neither of these work. (I don't know why they are not working).
N.B. The sole purpose of the environment is to return a variable (either an array or a string). In this environment array_pop(explode('/', __FILE__)) and end(explode('/', __FILE__)) both result in a browser error: Content Encoding Error An error occurred during a connection to example.com. Please contact the website owners to inform them of this problem.
Statements such asechoand other executing statements produce the same Encoding Error.
Here is my current code, which is working:
$My_Filename = explode('/', __FILE__)[(count(explode('/', __FILE__)) - 1)];
Is there a shorthand in PHP 7 to get the last value of an array?
You could do it with end
$latestValue = end($array); // Returns latest value in $array, setting the internal pointer to the last element
reset($array); // Don't forget to reset the pointer!!!
Ok... if it doesn't work (that's very strange dude, big problem xD) you could try something like:
$aux = array_values($array); //No need to use this if it isn't an associative array
$size = count($aux);
$latestValue = $array[$size-1]; //Be careful with empty arrays
You can use basename(__FILE__); to get last part of the path. In general don't strive for this kind of one-liners - you won't gain anything in terms of performance, but loose code readability.
Both array_pop() and end() functions with expressions will give you a notice, because they will also try to modify variable (passed by reference) in current scope, and the message you get is coming from server or error handler (hidden details in production environment) - default interpreter display (better for dev) would give you: <b>Notice</b>: Only variables should be passed by reference in....
Here's a possible candidate for the code golf:
Original: explode('/', __FILE__)[(count(explode('/', __FILE__)) - 1)] // 59 characters
Alternative: array_reverse(explode('/', __FILE__))[0] // 40 characters
That represents a reduction of nearly 33%.
I'm curious to know if there might be an even better shorthand.
I should firstly apologize for my probably rookie question, but I've just got no clue how to achieve that relatively complex task being a complete newbie regarding regex. What I need is to specify a validation pattern for a string input and perform separate checks on the separate segments of that pattern. So let's begin with the task itself. I'm working with php7.0 on laravel 5.4 (which should genuinely not make any difference) and I need to somehow produce a matching pattern for a string input, which pattern is the following:
header1: expression1; header2: expression2; header3: expression3 //etc...
What I'd need here is to check if each header is present and if it's present in a special validation list of available headers. So I'd need to extract each header.
Furthermore the expressions are built as follows
expression1 = (a1 + a2)*(a3-a1)
expression2 = b1*(b2 - b3)/b4
//etc...
The point is that each expression contains some numeric parameters which should form a valid arithmetic calculation. Those parameters should also be contained in a special list of available parameter placeholders, so I'd need to check them too. So, is there a simple efficient way (using regex and string analysis in pure php) to specify that strict structure or should I do everything step by step with exploding and try-catching?
An optimal solution would be a shorthand logic (or regex expression?) of a kind like:
$value->match("^n(header: expression)")
->delimitedBy(';')
->where(in_array($header, $allowed_headers))
->where(strtr($expression, array_fill_keys($available_param_placeholders, 0))->isValidArithmeticExpression())
I hope you can follow my logic. The code above would read as: Match N repetitions of the pattern "header: expression", delimited by ';', where 'header' (given that $header is its value) is in an array and where 'expression' (given that $expression is its value) forms a valid arithmetic expression when all available parameter placeholders have been replaced by 0. That's it all. Each deviation of that strict pattern should return false.
As an alternative I'm currently thinking of something like firstly exploding the string by the main delimiter (the semicolon) and then analysing each part separately. So I'll then have to check if there is a colon present, then if everything to the left of the colon matches a valid header name and if everythin to the right of the column forms a valid arithmetic expression when all param names from the list are replaced by a random value (like 0, just to check if the code executes, which I also don't know how to do). Anyway, that way seems like an overkill and I'm sure there should be a smoother way to specify the needed pattern.
I hope I've explained everything good enough and sorry if I'm being to messy explaining my problem. Thanks in advance for each piece of advice/help! Greatly appreciated!
Using eval() must always be Plan Z. With my understanding of your input string, this method may sufficiently validate the headers and expressions (if not, I think it should sufficiently sanitize the string for arithmetic parsing). I don't code in Laravel, so if this can be converted to Laravel syntax I'll leave that job for you.
Code: (Demo)
$test = "header1: (a1 + a2)*(a3-a1); header2: b1*(b2 - b3)/b4; header3: c1 * (((c2); header4: ((a1 * (a2 - b1))/(a3-a1))+b2";
$allowed_headers=['header1','header3','header4'];
$pairs=explode('; ',$test);
foreach($pairs as $pair){
list($header,$expression)=explode(': ',$pair,2);
if(!in_array($header,$allowed_headers)){
echo "$header is not permitted.";
}elseif(!preg_match('~^((?:[-+*/ ]+|[a-z]\d+|\((?1)\))*)$~',$expression)){ // based on https://stackoverflow.com/a/562729/2943403
echo "Invalid expression # $header: $expression";
}else{
echo "$header passed.";
}
echo "\n---\n";
}
Output:
header1 passed.
---
header2 is not permitted.
---
Invalid expression # header3: c1 * (((c2)
---
header4 passed.
---
I will admit the above pattern will match (+ )( +) so it is not the breast best pattern. So perhaps your question may be a candidate for using eval(). Although you may want to consider/research some of the github creations / plugins / parsers that can parse/tokenize an arithmetic expressions first.
Perhaps:
calculate math expression from a string using eval
How to evaluate formula passed as string in PHP?
Parse math operations with PHP
How to mathematically evaluate a string like "2-1" to produce "1"?
Any $pair that gets past the if and the elseif can move onto the evaluation process in the else.
I'll give you a headstart/hint about some general handling, but I'll shy away from giving any direct instruction to avoid the wrath of a certain population of critics.
}else{
// replace all variables with 0
//$expression=preg_replace('/[a-z]\d+/','0',$expression);
// or replace each unique variable with a whole number
$expression=preg_match_all('/[a-z]\d+/',$expression,$out)?strtr($expression,array_flip($out[0])):$expression; // variables become incremented whole numbers
// ... from here use $expression with eval() in a style/intent of your choosing.
// ... set a battery of try and catch statements to handle unsavory outcomes.
// https://www.sitepoint.com/a-crash-course-of-changes-to-exception-handling-in-php-7/
}
$test = "header1: (a1 + a2)*(a3-a1); header2: b1*(b2 - b3)/b4; header3: expression3";
$pairs = explode(';', $test);
$headers = [];
$expressions = [];
foreach ($pairs as $p) {
$he = explode(':', $p);
$headers[] = trim($he[0]);
$expressions[] = trim($he[1]);
}
foreach ($headers as $h) {
if (!in_array($h, $allowed_headers)) {
return false;
}
}
foreach ($expressions as $e) {
preg_match_all('/[a-z0-9]+/', $e, $matches);
foreach ($matches as $m) {
if (param_fails($m)) {
echo "Expression $e contains forbidden param $m.";
}
}
}
Regex appeared to be not as complicated as I thought when posting that question, so I've managed to achieve the pattern in its complete form by myself with the initial headstart owed to #mickmackusa. What I have finally come up with is that here, explained to you by regex101 itself: https://regex101.com/r/UHMrqL/1
The logic whic it's based on is described in the initial question. The only thing missing is the verification of the values of the headers and the names of the params, but that's easy to match afterwards with preg_match_all and verify with pure php checks. Thanks again for the attention and the help! :)
I have the following code as the only way I know to convert a float to a string with the fewest possible significant digits required to reproduce it (dtoa() with mode 4 in C).
$i = 14;
do {
$str = sprintf("%.{$i}e", $x);
$i++;
} while ($x != (float) $str);
The Hack typechecker reports an error because it expects the first parameter to sprintf() to be a literal string so it can check it against the arguments. Is there a way I can turn that off for this line?
Or is there another way I could achieve the same thing? Perhaps with the NumberFormatter class?
The typechecker has various methods of suppressing errors. The most appropriate in this case is probably HH_IGNORE_ERROR to suppress this particular error.
As written, your code will produce an error like Typing[4110] Invalid argument. Take the error code, in this case "4110", and use it to add the ignore annotation:
/* HH_IGNORE_ERROR[4110] Allow dynamic sprintf() explain explain etc */
$str = sprintf("%.{$i}e", $x);
I think your error code probably is exactly 4110, but I don't have the typechecker in front of me to verify for sure, make sure to use the right code from your error message.
Note that for technical reasons the parser is pretty finicky about HH_IGNORE_ERROR -- it must be a block-style comment with no extra whitespace from what I've written above, until after the final ] at which point you can write as much as you like in the comment explaining.
It was noted in another question that wrapping the result of a PHP function call in parentheses can somehow convert the result into a fully-fledged expression, such that the following works:
<?php
error_reporting(E_ALL | E_STRICT);
function get_array() {
return array();
}
function foo() {
// return reset(get_array());
// ^ error: "Only variables should be passed by reference"
return reset((get_array()));
// ^ OK
}
foo();
I'm trying to find anything in the documentation to explicitly and unambiguously explain what is happening here. Unlike in C++, I don't know enough about the PHP grammar and its treatment of statements/expressions to derive it myself.
Is there anything hidden in the documentation regarding this behaviour? If not, can somebody else explain it without resorting to supposition?
Update
I first found this EBNF purporting to represent the PHP grammar, and tried to decode my scripts myself, but eventually gave up.
Then, using phc to generate a .dot file of the two foo() variants, I produced AST images for both scripts using the following commands:
$ yum install phc graphviz
$ phc --dump-ast-dot test1.php > test1.dot
$ dot -Tpng test1.dot > test1.png
$ phc --dump-ast-dot test2.php > test2.dot
$ dot -Tpng test2.dot > test2.png
In both cases the result was exactly the same:
This behavior could be classified as bug, so you should definitely not rely on it.
The (simplified) conditions for the message not to be thrown on a function call are as follows (see the definition of the opcode ZEND_SEND_VAR_NO_REF):
the argument is not a function call (or if it is, it returns by reference), and
the argument is either a reference or it has reference count 1 (if it has reference count 1, it's turned into a reference).
Let's analyze these in more detail.
First point is true (not a function call)
Due to the additional parentheses, PHP no longer detects that the argument is a function call.
When parsing a non empty function argument list there are three possibilities for PHP:
An expr_without_variable
A variable
(A & followed by a variable, for the removed call-time pass by reference feature)
When writing just get_array() PHP sees this as a variable.
(get_array()) on the other hand does not qualify as a variable. It is an expr_without_variable.
This ultimately affects the way the code compiles, namely the extended value of the opcode SEND_VAR_NO_REF will no longer include the flag ZEND_ARG_SEND_FUNCTION, which is the way the function call is detected in the opcode implementation.
Second point is true (the reference count is 1)
At several points, the Zend Engine allows non-references with reference count 1 where references are expected. These details should not be exposed to the user, but unfortunately they are here.
In your example you're returning an array that's not referenced from anywhere else. If it were, you would still get the message, i.e. this second point would not be true.
So the following very similar example does not work:
<?php
$a = array();
function get_array() {
return $GLOBALS['a'];
}
return reset((get_array()));
A) To understand what's happening here, one needs to understand PHP's handling of values/variables and references (PDF, 1.2MB). As stated throughout the documentation: "references are not pointers"; and you can only return variables by reference from a function - nothing else.
In my opinion, that means, any function in PHP will return a reference. But some functions (built in PHP) require values/variables as arguments. Now, if you are nesting function-calls, the inner one returns a reference, while the outer one expects a value. This leads to the 'famous' E_STRICT-error "Only variables should be passed by reference".
$fileName = 'example.txt';
$fileExtension = array_pop(explode('.', $fileName));
// will result in Error 2048: Only variables should be passed by reference in…
B) I found a line in the PHP-syntax description linked in the question.
expr_without_variable = "(" expr ")"
In combination with this sentence from the documentation: "In PHP, almost anything you write is an expression. The simplest yet most accurate way to define an expression is 'anything that has a value'.", this leads me to the conclusion that even (5) is an expression in PHP, which evaluates to an integer with the value 5.
(As $a = 5 is not only an assignment but also an expression, which evalutes to 5.)
Conclusion
If you pass a reference to the expression (...), this expression will return a value, which then may be passed as argument to the outer function. If that (my line of thought) is true, the following two lines should work equivalently:
// what I've used over years: (spaces only added for readability)
$fileExtension = array_pop( ( explode('.', $fileName) ) );
// vs
$fileExtension = array_pop( $tmp = explode('.', $fileName) );
See also PHP 5.0.5: Fatal error: Only variables can be passed by reference; 13.09.2005