📜 ⬆️ ⬇️

New in Runkit 1.0.4: PHP 5.6+, closures everywhere and 12 more new features

Runkit 1.0.4 for PHP released!


Congratulations to all users of Runkit with a new long-awaited mega-release! If you constantly use Runkit and are well acquainted with its capabilities, history and development, then you can immediately proceed to the description of changes in release 1.0.4 . In any case, I propose to read the entire article.



What is Runkit?


Runkit is an extension of the PHP language that allows you to do things that are impossible from the point of view of this language. The expansion functionality consists of three parts.

Runtime manipulations


The first and largest part of the Runkit functional allows you to dynamically (during the execution of a PHP program) copy, modify and delete such entities that are not dynamically modified by the PHP language itself.

Runkit allows you to copy, redefine, and delete existing functions (including those built into the language), dynamically make a class a descendant of another class, inheriting all the content (runkit_class_adopt), or detaching the class from the parent, removing the inherited content (runkit_class_emancipate). You can also add, copy, override and delete methods of existing classes, add and delete their properties. In addition, Runkit allows you to override and delete previously defined constants.
')

Runkit_Sandbox


The second most part of the functionality is the sandboxes Runkit_Sandbox. They allow you to run a part of a program in PHP in an isolated environment. Each sandbox can have its own PHP security settings such as safe_mode, safe_mode_gid, safe_mode_include_dir, open_basedir, allow_url_fopen, disable_functions, disable_classes. In addition, each sandbox can customize Runkit functionality inside itself: put down its superglobal variables (which will be discussed below) and prohibit changing built-in functions.

Sandboxes can include PHP files (via include (), include_once (), require () and require_once ()), call functions inside themselves, execute arbitrary code in PHP, print the values ​​of their internal variables, complete their work. In addition, you can specify a function for intercepting and processing the sandbox output.

Inside the sandbox you can also create an anti-sandbox object Runkit_Sandbox_Parent for connecting the sandbox to the parent environment. The “anti-sandbox” functionality is very similar to the “sandboxes” functionality, but for security reasons, each function that connects with the external environment must be explicitly enabled when creating the sandbox.

Superglobals


Runkit also allows you to add new superglobal variables to PHP. To add such variables, it is enough to list their names separated by a comma in the runkit.superglobal property inside the PHP configuration file.

Other


In addition to the three main parts of the functional, Runkit also has tools for checking the syntax of code in PHP (runkit_lint and runkit_lint_file) and the function runkit_import, which allows you to import a PHP file like include, but ignoring all the global code in this file. Depending on the flags, runkit_import can import functions or classes (in whole or in part), overriding or saving existing ones.

Why do you need Runkit?


Runkit helps PHP programmers solve many different problems. I'll tell you about a few basic ones.

Patching someone else's programs


Imagine that you are using a third-party library (or framework) and at some point you need to change its behavior. However, the code that needs to be changed is in the private method of one of the library classes. The obvious solution is to edit the file containing this method. This is a working solution, but the library code is now changed and updating it becomes a troublesome task, because you will need to apply a patch every time you update the library. Another solution is to override the method of interest with Runkit, this is done with a single call to the runkit_method_redefine function. There is a similar solution for redefining functions already existing in the program (runkit_function_redefine) and constants (runkit_constant_redefine). Such a change of program code at runtime is called “monkey patching”. On specialized Internet forums, you can find various patching recipes using Runkit such libraries as WordPress, 1C-Bitrix, CodeIngniter, Laravel, etc. To solve some problems, it is useful to replace the functions built into the PHP language itself, and Runkit also knows how.

Isolated environment for running custom scripts


With the help of sandboxes, Runkit_Sandbox often make environments for executing custom code. When properly configured, this makes it possible to isolate user code from the main system. In its simplest form, it looks like this:

$options = […]; $sandbox = new Runkit_Sandbox($options); $sandbox->ini_set(…); $sandbox->eval($code); 

Other uses


With the help of runkit, you can also organize an update of the program code on the fly, as is done, for example, in phpdaemon (see habrahabr.ru/post/104811 ).

Unit tests


Runkit's ability to redefine functions and methods makes it extremely useful when writing unit tests in PHP. Using Runkit, making test twins (stubs or “spies”) during test execution becomes easy, even if the architecture of the tested code does not support dependency injection . There are ready-made libraries that implement the substitution of PHP methods and functions for stubs in the context of unit tests (for example, ytest , phpspy, and others). With the right library choice, you can get amazingly simple tests (see for example, here ).

Runkit development history


Start


Runkit was created in 2005 by Sara Golemon . The latest author's release (version 0.9) was released on 06.06.06. In October of 2006, Sarah stopped supporting the expansion without releasing version 1.0. At that time, Runkit contained functions for manipulating constants, functions, methods, runkit_import, the function of adding properties to classes, syntax checking functions, sandboxes, and superglobal variables. The documentation on the php.net website ( http://php.net/runkit ) froze in the region of version 0.7, so that even some of the functions that Sarah herself has done are still not described. In addition, in this documentation all the functionality of Runkit is called experimental, which was relevant in 2006, but is absolutely not true now.

Decay


From October 2006 to October 2009, the extension was not supported by anyone, and the PHP language went ahead, which is why, despite edits from members of the PHP community, already in version PHP 5.2, Runkit was unstable and caused segmentation errors.

Revival


In October of 2009, I began to repair Runkit, and then develop it at https://github.com/zenovich/runkit . I'll tell you what releases were released during this time and what changes are included in them.

Release 1.0.0 (April 1, 2010)


In fact, this release has never happened, it is fictitious :). This includes all community edits since the release of version 0.9 and prior to release 1.0.1.

Release 1.0.1 (October 3, 2010)


The first real release of Runkit after 2006. Now Runkit supports all versions of PHP up to 5.3 inclusive. Fixed more than ten serious errors, including those that led to PHP crashes. The main ones are:

Release 1.0.1 adds the ability to define and modify static methods using the new constant RUNKIT_ACC_STATIC:

 runkit_method_add('MyClass', 'newMethod', '$arg1, $arg2', '/* some code here*/', RUNKIT_ACC_STATIC); 

Also added the ability to import static class properties. When importing a class, static properties will be copied by default, but you can disable their import using the new RUNKIT_IMPORT_CLASS_STATIC_PROPS constant:

 runkit_import('myfile.inc', RUNKIT_IMPORT_CLASSES); //    runkit_import('myfile.inc', RUNKIT_IMPORT_CLASSES & ~ RUNKIT_IMPORT_CLASS_STATIC_PROPS); //  ,       runkit_import('myfile.inc', RUNKIT_IMPORT_CLASS_STATIC_PROPS); //      

In addition, the release added the ability to apply a closure to the sandbox using Runkit_Sandbox :: call_user_func ().

Release 1.0.2 (October 5, 2010)


Bug fix of the previous release. Improved compatibility with PHP 5.3.

Release 1.0.3 (January 2, 2012)


Fixed inheritance when renaming methods using runkit_method_rename. Fixed build extensions under Windows.

Release 1.0.4 (September 25, 2015)


Long-awaited Mega-Release! Full PHP5 support (up to PHP 5.6 inclusive).

A lot was done in this release to stabilize Runkit: tests were run for each version of PHP in four versions: with and without ZTS, with and without valgrind. Virtually every change added new tests. Thanks to this, it was possible to identify and correct a huge number of errors.

Among the important fixes are the following:

In total, more than forty (!!!) important corrections were made in the release, their full list can be found in the package.xml file.

Now I’ll tell you about the main functional changes.

Functions and methods

Closures

For PHP 5.3+, the runkit_function_add, runkit_function_redefine, runkit_method_add, and runkit_method_redefine functions now support closure as parameters. For example, if earlier, to override a function, you had to write an expression like

 runkit_function_redefine('sprintf', '$s', 'return $s;'); 

which used eval to turn a string into bytecode, which is very slow, you can now write

 runkit_function_redefine('sprintf', function($s) {return $s;}); 

No eval is executed at the same time, besides it is much easier to maintain such code - there are no more program parts inside string literals! The same goes for the runkit_function_add, runkit_method_add and runkit_method_redefine functions.

Magic methods

Also in Runkit, manipulations with the magic methods __get, __set, __isset, __unset, __clone, __call, __callStatic, serialize, unserialize, __debugInfo and __toString are now fully supported. The same applies to constructors and destructors as with the modern method of naming, and with the name in the style of PHP4.

Doc-comments

Now, when adding or redefining methods and functions using the old syntax (when the arguments of the new function and its body are passed as strings), you can specify doc-comment. For this purpose, the functions runkit_function_add, runkit_function_redefine, runkit_method_add and runkit_method_redefine have a new optional (last in order) argument - doc_comment:

 runkit_method_redefine('MyClass','myMethod', '$arg', 'return $arg', RUNKIT_ACC_PRIVATE, 'my doc_comment'); //     doc-comment' runkit_method_add('MyClass','myMethod2', '$arg', 'return $arg', NULL, 'my doc_comment2'); //     doc-comment' 

When defining functions and methods in a new style (through closures), doc-comments can be specified in the same way as when defining normal functions — via comments above the function body. Both methods can be combined - the priority of doc-comment passed through an argument. In addition, the placement of doc-comments for inheritance, copying and renaming of methods and functions was repaired.

Return values ​​by reference

Added the ability to add and override functions and methods so that a new function (or method) returns a value by reference. In order for the new function specified using the old syntax (when the arguments of the new function and its body are passed in strings) return a value by reference, you must pass to the runkit_function_add function (or runkit_function_redefine) a new argument — return_ref — with the value TRUE. For example,

 runkit_function_redefine('my_function', '$a', 'return $a;', TRUE); //     

If the method is similarly added (or redefined), the flags argument is used with the RUNKIT_ACC_RETURN_REFERENCE bit set. For example,

 runkit_method_redefine('MyClass', 'myMethod', '$a', 'return $a;', RUNKIT_ACC_PROTECTED | RUNKIT_ACC_RETURN_REFERENCE); // protected-     

If you define a function or method using the new syntax (via closures), then you do not need all these flags - just add an ampersand before the function argument list:

 runkit_function_redefine('my_function', function &($a) {return $a;}); //     

Class properties

The internal implementation of class property manipulations has been completely reworked. Adding, deleting and importing class properties is now correctly reflected in descendant classes. Moreover, now these actions can influence objects of a class and its descendants. To enable this effect, you need to set the RUNKIT_OVERRIDE_OBJECTS bit in the flags argument when calling the runkit_default_property_add and runkit_default_property_redefine functions. For example,

 runkit_default_property_add('MyClass', 'newProperty', 'value'); //         runkit_default_property_add('MyClass', 'newProperty', 'value', RUNKIT_OVERRIDE_OBJECTS); //        -,      

The same applies to the import of class properties:

 runkit_import('myfile.inc', RUNKIT_IMPORT_CLASS_PROPS); //   ,         runkit_import('myfile.inc', RUNKIT_IMPORT_CLASS_PROPS | RUNKIT_IMPORT_OVERRIDE); //   ,   ,     runkit_import('myfile.inc', RUNKIT_IMPORT_CLASS_PROPS | RUNKIT_IMPORT_OVERRIDE | RUNKIT_OVERRIDE_OBJECTS); //   ,         

In addition, a new runkit_default_property_remove () function has been added to remove properties from classes. To remove a property not only from the class, but also from its objects, the runkit_default_property_remove function has a third optional parameter:

 runkit_default_property_remove('MyClass', 'myProperty'); //    ,      runkit_default_property_remove('MyClass', 'myProperty', TRUE); //         

Also now, when adding and redefining class properties, now you can use not only scalar values, but also arrays.

Classes

Previously, the runkit_class_adopt and runkit_class_emancipate functions altered the contents of the classes, but did not affect their hierarchy (that is, after using runkit_class_adopt, the class still formally had no parent, but the parent still runnit_class_emancipate). Now it is fixed.

Register in names of entities and namespaces

Working with constants, functions, methods and properties now fully supports namespaces. Runkit also stopped translating the names of the properties, classes, methods and functions it creates (as it was before) into lowercase.

Additional safety sandboxes

For the Runkit_Sandbox sandboxes, you can now disable the allow_url_include INI setting. Also now, regardless of the platform, the open_basedir setting supports path lists (previously only one path could be entered).

Updates

Upgrading Runkit has become much easier. Now it can be done in the usual way for all PECL users through the official channel pecl.php.net . To install the latest release of Runkit, just dial

 pecl install runkit 

In addition, all archives with releases are now available at http://pecl.php.net/runkit .

Conclusion


Now Runkit is used in many well-known companies and projects around the world, both for unit testing and for many other tasks. I am sure that there is a great future ahead of him. This will be possible thanks to donations, which can now be done with a single click from the page of the github.com/zenovich/runkit project or directly from phpinfo ().

Thank!

Source: https://habr.com/ru/post/267495/


All Articles