I want to build a PHP extension that can dynamically inspect every opcode generated from a PHP file and do some checking on that.
I came across several websites and found out a couple of functions like zend_set_user_opcode_handler, but I fail to understand how this function can be used to get a complete opcode like ASSIGN !0, 50.
I'm aware of a command like php -d vld.active=1 -d vld.execute=0 -f [filename].php which I can use to generate PHP opcodes, but as far as I know you can only access the opcodes after the PHP program terminates.
What I'd like to get from the extension is an opcode which is obtained one-by-one (dynamically) as the function executes.
Can someone help me with this issue?
You could use parsekit which is available through pecl which can be downloaded from the pecl website or installed with:
sudo pecl install parsekit
Get OPcodes from a string of PHP code during runtime:
You could use the parsekit_compile_string
The syntax for this command is:
array parsekit_compile_string ( string $phpcode [, array &$errors [, int $options = PARSEKIT_QUIET ]] )
Parameters:
phpcode
A string containing phpcode. Similar to the argument to eval().
errors
A 2D hash of errors (including fatal errors) encountered during compilation. Returned by reference.
options
One of either PARSEKIT_QUIET or PARSEKIT_SIMPLE. To produce varying degrees of verbosity in the returned output.
Return Values
Returns a complex multi-layer array structure as detailed below.
An example usage of this is:
<?php
$ops = parsekit_compile_string('
echo "Foo\n";
', $errors, PARSEKIT_QUIET);
var_dump($ops);
?>
The output is too long to include in this answer but is available on the documentation page
Get OPcodes from a PHP file during runtime:
You could use the parsekit_compile_file
Very similar to the above approach but parses a file instead of a string.
Related
With PHP 7.3, I'm trying to use the PECL stats extension: https://pecl.php.net/package/stats
Especially, I'm interested in generating random values following a normal distribution, with the function stats_rand_gen_normal
In my script, the function always returns the same result.
I tried in CLI, to be sure :
php -r "echo(stats_rand_gen_normal(0,1));"
Every execution prints the same number.
So it's a random value which is always the same... What am I missing here?
This seems to me like a bug too and I have make a bug report.
There is a solution on stackoverflow using a custom function.
You should use stats_rand_setall() to seed values to the random generator.
I have a php script. I want to check the php version compatibility that each function of this script has.
For example the script has the hash() function. If we look at php.net site it says that this function was added (PHP 5 >= 5.1.2, PECL hash >= 1.1).
So assuming that this script has only this function we say that it is compatible with php 5.1.2 and onwards. Is there any software tool that can scan the script and tell you the changelog of every function and possibly gives you the lowest possible score e.g. (php version 4.3 and on)? It is a bit tedious to check manually every function in the script.
Thank you in advance
As PHP supports dynamic function/method names, just searching through the source code would not give reliable results.
Example:
$function = 'strpos';
$result = $function(...);
You might use xdebug function traces together with a 100% test coverage. This could give you all function names which had been called during a test suite run with 100% coverage - meaning all code has run. But even this isn't reliable as you would have to make sure, that your test suite covered all possible dynamic function names what is impossible. (assuming the max length of a function name is unlimited)
My final answer is: this isn't reliably possible.
I'm working on some code that reads from a socket, and it goes wrong when it gets a certain large input. I went to add a unit test for this, before fixing it, but got stuck because I cannot mock fread (and the other PHP built-in functions I'm using such as fsockopen, feof, etc.).
In simple terms my problem is that this code fails with "Fatal error: Cannot redeclare fgets() ...":
function fgets($fp){
return "xxx";
}
I realize I could create a socket wrapper class, that my real code uses, and then I could create a mock object for that wrapper class. But that is The Tail Wagging The Dog, and I can think of reasons it is a bad idea, beyond just the principle of the thing. (E.g. Making code more complex, efficiency, having to refactor code not under test yet.)
So, my question is how can I replace the built-in fgets() implementation with my own, within a unit test? (Or, if you want to think outside the box, the question can be phrased as: how can I control the string that a call to fgets($socket) returns, when $socket is the return value from a call to fsockopen?)
ASIDE
Installing apd, as required by the correct answer, is hard work; it was last released in 2004, and does not support php 5.3 out of the box. No Ubuntu package for it and also pecl install apd failed. So here are the procedures to install it (these are for ubuntu 10.04) (all done as root):
pecl download apd
tar xzf apd-1.0.1.tgz
cd apd-1.0.1
phpize
./configure
# Do edits (see below)
make install
See https://bugs.php.net/bug.php?id=58798 for the patch you need to do. NB. there is only one line you really have to change, so you can do it by hand, as follows: open php_apd.c in a text editor, go to line 967, and replace the CG(extended_info) = 1 line with this one:
CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
Finally, you need to add a php.ini entry to activate it. See http://php.net/manual/en/apd.installation.php
If you don't have access to APD or Runkit but are using namespaces, try this: https://stackoverflow.com/a/5337635/664108 (Answer in link refers to time() but it makes no difference)
Have a look at these:
bool rename_function ( string $original_name , string $new_name )
bool override_function ( string $function_name , string $function_args , string $function_code )
Change fsockopen to fopen to do mock, and don't change any other functions.
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
to
$fp = fopen("/path/to/your/dummy_data_file");
I blogged about my experiences which contains full working code showing how to use override_function to achieve the desired goal; both file reading and socket reading. I won't repeat that whole article here, but will just point out how you have to use the functions:
Use rename_function to give a name to the old, original function, so
we can find it later.
Use override_function to define the new behaviour
Use rename_function to give a dummy name to __overridden__
Step 1 is critical if you want to be able to restore the original behaviour afterwards.
In the Food For Thought section at the end I show an alternative approach that I think is more robust, and therefore I think it is safer for replacing file functions when using phpUnit. It creates a permanent function replacement who's default behaviour is to forward to the built-in function. It then checks parameters (the resource $handle in this case) to decide if it is being called on a stream we want different behaviour for. (I think you could call this an example of the Chain Of Responsibility design pattern.)
I am trying to share a Memcache key between Python and PHP. Python writes the key and PHP reads it. I am using the Cakephp framework, with php-pecl-memcache (not php-pecl-memcached), and the python-memcache all python library.
Python:
mc = memcache.Client( ["127.0.0.1:11211"])
key = "key1"
value = 1323779849
mc.set(key, value)
PHP:
echo Cache::read('key1', 'memcached');
PHP can't read the variable, I get weird "MemcachePool::get() [http://php.net/memcachepool.get]: Failed to uncompress data" errors; I suspect it has to do with memcached flags that are set differently in each library.
This is what happens when I telnet to memcached:
Python sets key:
get key1
VALUE key1 1 12
1323779849
.
END
PHP sets key:
get key1
VALUE key 1 0 12
1323779849
END
Is there a way to override these flags and just 'do your own thing'?
Are there php/python memcache libraries that play better together?
Alternatively, is there a simple way to create a common memory space between python/php?
Finally got it to work. Lot's of stuff wasn't working as expected.
One problem is that php and python use different flags to do different things.
Not a problem in an all-python or all-php solution, but for inter-environment communication a real problem. A useful resource is http://www.hjp.at/zettel/m/memcached_flags.rxml, which shows that python-memcache flags long integer as '2', which php-memcache does not understand, hence the compression error. I amended python-memcache to include a 'flag-override' in the set function. This variable simply forces a particular flag irrespective of what python memcache thinks it ought to be. This allowed me to re-flag Int from 2 to 0. I will prob branch the current version of python-memcache and submit it to Github. This allowed me to force the python long int flag (2) to something php would understand (0).
CakePhp prior to 1.3.3 stores all keys in memcached with an additional key_expires key, flagged as 768, etc, etc. Without this additional key it cannot find the key you are looking for. Thankfully this behaviour was dumped in later Cakephp version (I simply upgraded to 1.3.13) and it works well now.
When you put something via python memcached, it's probably pickled. So PHP cannot unpickle it. I would try to use some kind of very basic type maybe ctypes? Maybe raw strings?
I had a similar problem, using PHP and pymemcache. I serialized Python dictionary with https://www.php2python.com/wiki/function.serialize/ and wrote that to memcache. PHP side also had it's own way of storing into memcache, and memcache values written by PHP and Python seemed to be the same, but PHP couldn't read Python set value properly, so it puzzled me a lot. PHP read it as a String, being unable to deserialize it / convert it to an Array. Then I got to read memcache values using netcat, like this:
echo -e 'get my-key\r' | nc 192.168.1.17 11211
Python set value returned:
VALUE my-key 0 1460
, while PHP set value had:
VALUE my-key 1 1460
Not knowing how to deal with these flags, I simply used this - on PHP side, if I got a String "a:{s:6..." instead of an Array, I used PHP's unserialize() method to make it an Array and it worked.
I have a php script that handles a form input. For design reasons both a bit out of my control, and which I do not entirely wish to change, I have to call a perl script with the parameters specified in the html form.
I sanitized all inputs and then output them to a file called input, which is read by the perl script named, for sake of brevity in this question, script.pl. Script.pl should do some stuff and then write all outputs to a file named output.
I call the perl script from php like so:
system('perl script.pl 2>errors');
No good, nothing happens. output is not created, errors is not created, and the side effect does not occur.
My apache runs as www-data user and group id. My directory is set with 775 settings with ownership as me:www-data. (My user name is replaced with "me" for sakes for privacy).
My question is two fold:
1) Am I doing this wrong? If so how should I improve upon the code?
2) Is there a more sane way to catch errors in system execution?
After programming in perl for a while, php feels like a pain in the ass.
OS: Ubuntu server edition
popen can be used to get the shell's response. that is your best bet. Which can help you debug why system is angry. also, if your pl is saying "hello" and "bye", popen can even read that.
If the command to be executed could not be found, a valid resource is returned. This may seem odd, but makes sense; it allows you to access any error message returned by the shell
Ideally, I would have taken data from stdin and written to stdout. popen would allow neat access to both.
popen('pwd;perl script.pl 2>errors;echo done');
then you can see where were you (directory) when system got called and did it "done".
In the past I have used shell_exec() or backticks to accomplish this.
The documentation for shell_exec's return value indicates it is identical to the backtick operator:
Return Values
The output from the executed command.
Hope that helps.
system() only returns the status code.
$var = shell_exec ("ls");
print $var;
$var = `ls -l`;
print $var;
Is perl in the path? Maybe you need to specify it fully (e.g. /usr/bin/perl). Is system() returning false, indicating a failure? If you try something simpler, like system('/usr/bin/true', $retval), does $retval get set to 1?
Take a look at the PHP system() documentation. The following is the function prototype of system():
string system ( string $command [, int &$return_var ] )
Pass in a 2nd argument and then print out the return string as well as the second variable. See what the error says.