This time I bring you a fun extension. We know that when PHP is running, that is, after deployment is completed, we cannot modify the values of constants, nor can we modify the implementation inside the method body. In other words, after we complete the coding, we upload the code to the server. At this time, we cannot modify the value of a constant without modifying the code. The constant itself cannot be modified. However, the runkit extension can help us accomplish this function.
[Recommended: PHP video tutorial]
Dynamic modification of constants
define('A', 'TestA'); runkit_constant_redefine('A', 'NewTestA'); echo A; // NewTestA
Isn’t it amazing? This runkit extension is a functional extension that allows us to dynamically modify some constants, method bodies, and classes at runtime. Of course, from a system security perspective, this extension is not highly recommended. Because the meaning of a constant is an unchanging quantity, it should not be modified. In the same way, dynamically changing the content of a function body or class definition at runtime may affect other code that calls these functions or classes. Therefore, this extension is a dangerous extension.
In addition to dynamically modifying constants, we can also use the runkit_constant_add() and runkit_constant_remove() functions to dynamically add or delete constants.
Installation
To install the runkit extension, you need to download it from github and then compile the extension normally. The one downloaded from pecl is outdated.
PHP5: http://github.com/zenovich/runkit
PHP7: https://github.com/runkit7/runkit7.git
clone after success Just follow the normal extension compilation and installation steps.
phpize ./configure make make install
Different PHP versions need to install different versions of extensions. At the same time, runkit7 is still under development, and some functions are not yet supported, such as:
- runkit_class_adopt
- runkit_class_emancipate
- runkit_import
- runkit_lint_file
- runkit_lint
- runkit_sandbox_output_handler
- runkit_return_value_used
- Runkit_Sandbox
- Runkit_Sandbox_Parent
When writing the test code for this article, the above functions or classes were not supported. You can use the PHP5 environment to test whether the original extensions can be used normally.
View super global variable keys
print_r(runkit_superglobals()); //Array //( // [0] => GLOBALS // [1] => _GET // [2] => _POST // [3] => _COOKIE // [4] => _SERVER // [5] => _ENV // [6] => _REQUEST // [7] => _FILES // [8] => _SESSION //)
This function actually checks all super global variable key names in the current running environment. These are some of our commonly used superglobal variables, so I won’t explain them one by one.
Method related operations
Method operations are the same as constant operations. We can dynamically add, modify, delete and rename various methods. First, let’s take a look at what we are most concerned about when modifying the logic code in the method body during dynamic runtime.
function testme() { echo "Original Testme Implementation\n"; } testme(); // Original Testme Implementation runkit_function_redefine('testme','','echo "New Testme Implementation\n";'); testme(); // New Testme Implementation
Define a testme() method, and then modify its implementation through runkit_function_redefine(). Finally, when testme() is called again, the output will be the newly modified implementation. So, can we modify the methods that come with PHP?
// php.ini runkit.internal_override=1 runkit_function_redefine('str_replace', '', 'echo "str_replace changed!\n";'); str_replace(); // str_replace changed! runkit_function_rename ('implode', 'joinArr' ); var_dump(joinArr(",", ['a', 'b', 'c'])); // string(5) "a,b,c" array_map(function($v){ echo $v,PHP_EOL; },[1,2,3]); // 1 // 2 // 3 runkit_function_remove ('array_map'); // array_map(function($v){ // echo $v; // },[1,2,3]); // PHP Fatal error: Uncaught Error: Call to undefined function array_map()
The comments in the code make it very clear. We only need to set runkit.internal_override=1 in php.ini to dynamically modify the methods and functions that come with PHP. For example, in the first paragraph, we modified the str_replace() method so that it can directly output a paragraph of text. Then we rename implode() to joinArr() so that we can use this joinArr() just like implode(). Finally, we removed the array_map() method. If this method is called again, an error will be reported.
Class method related operations
The operation of the internal method function of the class is similar to the operation of the variable method above, but we cannot modify the classes that come with PHP. You can try this yourself.
//runkit_method_add('PDO', 'testAddPdo', '', 'echo "This is PDO new Func!\n";'); //PDO::testAddPdo(); // PHP Warning: runkit_method_add(): class PDO is not a user-defined class
It can be seen from the error message that the PDO class is not a user-defined class, so the runkit function cannot be used for related operations. Then let's take a look at how our custom classes use runkit to perform dynamic operations.
class Example{ } runkit_method_add('Example', 'func1', '', 'echo "This is Func1!\n";'); runkit_method_add('Example', 'func2', function(){ echo "This is Func2!\n"; }); $e = new Example; $e->func1(); // This is Func1! $e->func2(); // This is Func2! runkit_method_redefine('Example', 'func1', function(){ echo "New Func1!\n"; }); $e->func1(); // New Func1! runkit_method_rename('Example', 'func2', 'func22'); $e->func22(); // This is Func2! runkit_method_remove('Example', 'func1'); //$e->func1(); // PHP Fatal error: Uncaught Error: Call to undefined method Example::func1()
We defined an empty class, then dynamically added two methods to it, then modified method 1, renamed method 2, and finally deleted method 1. The series of operations are actually the same as above The operation of the ordinary method is basically the same.
Summary
As mentioned above, this extension is a relatively dangerous extension, especially if runkit.internal_override is turned on, we can also modify PHP's native functions. But if you must use it, then its functions are very useful. Just like the visitor pattern, "most of the time you don't need the visitor pattern, but when you need the visitor pattern, you really need it." The same is true for this set of runkit extensions.
Test code:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202006/source/%E4%B8%80%E8%B5%B7%E5%AD%A6%E4%B9%A0PHP%E7%9A%84runkit%E6%89%A9%E5%B1%95%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8.php