📜 ⬆️ ⬇️

Hot code replacement (code hot swapping) in PHP

The weather outside the window just requires something hot, so taking advantage of the opportunity to explore something in my spare time, I decided to think - is it possible, without stopping the script, to change the function that is being performed? I met this demand a little earlier when developing our startup. We had one of the internal servers, which managed all the actions between users in real time. This is a common PHP daemon router that processed requests from client requests (inside the server), but there was one difficulty — when I changed something in the server code or handlers of individual commands, the daemon had to be rebooted, which meant that current clients were disconnected and loss of information about the state of the server (this issue is solved, of course). The same was in case of an error in the code - all connected users immediately felt it on themselves (well, that they are all the same developers, and not real customers). Can this be avoided?

Of course, you can, for example, rejecting the demon scripts, as we did. However, this problem did not disappear, it just moved it to another plane. After all, there are other services demons, which also have to work continuously, even though their significance is less, however, I don’t really want to reboot each time. Therefore, I decided to look for an opportunity to connect a new code on the fly and immediately execute it. The minimum code will be a function, but it is quite possible to include class methods.

Another point is not quite clean replacement. First, the old method remains in memory and is still available for execution, on a par with the new one. Secondly, the function can be changed only at the moment of its calling, in the process of execution it will not work anymore.
')
There are languages ​​in which hot-swappable code is embedded at the language or platform level. The most famous representative is probably you guessed it - Erlang / OTP. This possibility is also present in other languages ​​- Smalltalk, Lisp and Java (albeit with a number of restrictions). In Visual Studio, this is possible when using debug mode and is supported in C #, VB.NET, and C / C ++. However, we have the same PHP ...

hotswap


At once I will make a reservation that this is just an experiment, checking the very possibility of such a trick, although I do not rule out a real use. Of course, the speed in this mode is reduced, but if the overhead of each call is tolerable, we will be able to transparently update the working PHP-daemon without affecting the entire code. In addition, it is possible to first create an application framework, run it, and then gradually build on the implementation of each method.

Let's start. The first thing that comes to mind is - what if just simply calling the connection of the file with the function text again? Unfortunately, this will not work directly - caching accelerators can interfere, but this is the least of all problems. The main thing is that PHP generates an error when trying to connect a function with the same name as the existing one at the time of connection. Conclusion? Changing the name of our function each time we update its code, for example, adding a suffix with the version number: if the original function has the form function _my_test (), then in the case of editing a new function should already be called _my_test_v1 (). But the rest of the code does not know that we have connected a new implementation.

One of the ways is that the script at runtime can get a list of all functions defined in scripts (separately system and user-defined) and look in this list, all of a sudden there is an updated function (that is, ordinary string methods). But this is not the best method, and besides, there is enough invoice for the costs of processing an array of functions, in large projects it can be significant. But it works, which was tested on the first evening of the study.

And later the thought came to mind how to further improve the method of determining a new method. Let him inform the rest of the code that he has been updated - because who, apart from the function itself, knows this best of all?

The opportunity to set the return value in the source code file, which is connected with include / require, came to the rescue. Through return, you can send and receive any information at the time of connection. Including the table of certain functions and their versions, as well as aliases - to hide a specific version name we use an alias, which is already resolved inside to a specific name of the function that is called.

This is followed by another magic property of PHP - namely, __call () - the magic method of objects, which is called if we turn to an unknown class method. And now we do this - we define a special proxy class, which has only one method - this very __call (). In the surrounding code, all calls to functions that change without reloading the script are rewritten to calls to the methods of our proxy class. Of course, aliases of real methods are called. This means that no matter how much we change the name of the method, _my_test1, _my_test99, in our code we simply refer to a common alias, _my_test, which hides the real call.

The __call method, when called, checks first of all whether the file has been updated since the last access. To do this, we first clear the internal cache of the file system information (using the clearstatcache function), then we get the time stamp of the file change via filemtime. If the file is updated, you need to connect it through require. It is important here - the file will give us a table of the functions and their versions defined there, so we will first check if the method we need is implemented there. The name of the requested method is passed to the __call magic method by the first parameter, so it is enough just to check for the presence of such a key in the resulting array. If it is, then check the version number of the current code and the connected one. Our version is an integer or fractional number - so that you can find out by simple comparison which version is greater. Of course, versions should monotonously increase with each edit of the source code of the function.

If we find the desired function and its version is larger than the current one, we execute. The transferred array contains a string with the real name of the new function, so you need to call it via call_user_func, passing as a parameter the real name of the new function and the parameters with which the proxy class method was called. Before that, we will definitely keep all information inside the proxy - the timestamp of the connected file, the table of functions and their versions. Now we will have a table about which versions of the functions are relevant at the current moment and we can simply compare them with the next call to decide what to call. Note that the source file is only connected if it has been changed, but if you do not change the name of the functions, there will be an error (Fatal error: Cannot redeclare _myfn_1 () (previously declared in ...), because PHP will see that we are trying to connect an existing function. Therefore, it is imperative to change the names of functions and increase the version number every time the file is changed. The problem may be to define several functions in one file - you have to rename all methods regardless of whether their code was actually changed. However, if you rename only the function, but in the method table, do not touch anything, then everything is OK, the previous method that is already in the interpreter’s memory will be called anyway, so changing the name in the function table is possible only when it has really changed.

This is how you can substitute methods in a running daemon on PHP without the need to stop it. Each time the method is called, it will be checked if there were any code changes and if something has changed, we try to call the new version of the function.

I would like to see the loss of productivity at check. This is not a real test, just for the sake of "eye evaluation".

An example of a normal call:
class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  1. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  2. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  3. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  4. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  5. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  6. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  7. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  8. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  9. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  10. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  11. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  12. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  13. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  14. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  15. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  16. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  17. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  18. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  19. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  20. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  21. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  22. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  23. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
  24. class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .
class Test_1 { public function _myFn($mv){ $_rand = rand(0, 99999999); $_arr = get_defined_functions(); return sha1($_rand . $mv . implode( ',' , $_arr[ 'internal' ])); } } $i = 0; $max = 10000; $_st = microtime( true ); $test = new Test_1(); while ($i < $max) { $test->_myFn($i); $i++; } $_ft = microtime( true ); echo "\n Total: " . round(($_ft - $_st), 6) . " sec; \n" ; echo "Per call: " . round((($_ft - $_st)/$max), 6) . " sec \n" ; /* * Total: 28.779473 sec; * Per call: 0.002878 sec */ * This source code was highlighted with Source Code Highlighter .

Without code substitution at runtime (but through our proxy class, with checks):
  1. / *
  2. * Total: 28.914997 sec;
  3. * Per call: 0.002891 sec
  4. * /
* This source code was highlighted with Source Code Highlighter .

And now the result with the substitution of the source function at runtime (I just edit the file and upload it to the server in parallel with the script running in the console):
  1. / *
  2. * Total: 32.493496 sec;
  3. * Per call: 0.00065 sec
  4. * /
* This source code was highlighted with Source Code Highlighter .

Although the given estimate of the time for one call increased almost twice, the total time for performing the same test (calling 10 thousand times of the method) increased slightly - by about 13 percent. Probably because during the test the method was changed only once, all before and subsequent executions in the cycle were without accessing the file with the code, so the frequency of updating the source codes and the intensity of their call inside the application will play a major role in slowing down. But for normal cases, when the code is relatively rare (that is, time measured by seconds between calls), and the calculations themselves inside the code being replaced are more or less significant (for example, receiving a file from a remote server), the losses will be almost imperceptible, but the flexibility of the application will increase .

Of course, there are many nuances and objections - and in the real practice of fault-tolerant systems, I would carefully apply this, but the fact remains that hot-code replacement in PHP applications is quite possible! What I wanted to find out.

PS And here is the source code itself: a proxy class and a plug-in file with the function:

  1. / *
  2. * Proxy class and test code
  3. * /
  4. class HotCode
  5. {
  6. protected $ _current_v = null ; // current code version
  7. protected $ _current_timestamp = 0; // timestamp of the function that is executed
  8. protected $ _current_fn = null ; // name of the current function
  9. public function __call ($ fnmame, $ fparam)
  10. {
  11. if (($ this -> _ current_fn! = null ) && ($ this -> _ current_timestamp! = null ))
  12. {
  13. echo $ this -> _ current_fn. "from" . date ( 'jmY H: i s' , $ this -> _ current_timestamp). "is current function \ n" ;
  14. }
  15. clearstatcache ();
  16. // check, can there be current code?
  17. $ _file_last = filemtime ( '/var/www/deamon/hot_swap_fn.php' );
  18. if ($ _file_last === FALSE)
  19. {
  20. // what = something wrong
  21. throw new Exception ( 'Including file does not exist' );
  22. }
  23. echo "Last modif of file:" . date ( 'jmY H: i s' , $ _file_last). "\ n" ;
  24. echo "Now:" . date ( 'jmY H: i s' , time ()). "\ n" ;
  25. if (($ this -> _ current_v == null ) || ((is_integer ($ _ file_last))) && ($ _file_last> $ this -> _ current_timestamp)))
  26. {
  27. // forcefully connect
  28. $ _xfn = require ( 'hot_swap_fn.php' );
  29. // we got an array of functions defined there, their versions and aliases
  30. if (array_key_exists ($ fnmame, $ _xfn))
  31. {
  32. // method is
  33. if ($ this -> _ current_v == null )
  34. {
  35. // first time, yeah
  36. $ this -> _ current_v = $ _xfn [$ fnmame] [ 'v' ]; // remember the version
  37. $ this -> _ current_fn = $ _xfn [$ fnmame] [ 'fn' ];
  38. $ this -> _ current_timestamp = $ _file_last;
  39. }
  40. else
  41. {
  42. // compare versions
  43. if ($ _xfn [$ fnmame] [ 'v' ]> $ this -> _ current_v)
  44. {
  45. // newer version
  46. $ this -> _ current_v = $ _xfn [$ fnmame] [ 'v' ]; // remember the version
  47. $ this -> _ current_fn = $ _xfn [$ fnmame] [ 'fn' ];
  48. $ this -> _ current_timestamp = $ _file_last;
  49. }
  50. // otherwise I use the old one
  51. }
  52. // Call with parameters
  53. if ($ this -> _ current_fn! = null )
  54. return call_user_func ($ this -> _ current_fn, $ fparam [0]);
  55. else
  56. return $ this -> defaultEmptyFnCall ($ fparam); // otherwise default "stub"
  57. }
  58. else
  59. return $ this -> defaultEmptyFnCall ($ fparam);
  60. }
  61. else
  62. if ($ this -> _ current_fn! = null )
  63. return call_user_func ($ this -> _ current_fn, $ fparam [0]);
  64. else
  65. return $ this -> defaultEmptyFnCall ($ fparam); // otherwise default "stub"
  66. }
  67. // call if requested a function that does not exist
  68. private function defaultEmptyFnCall ($ param = null )
  69. {
  70. var_dump ($ param);
  71. }
  72. }
  73. $ _my_hot_class = new HotCode ();
  74. <? php
  75. / * File: hot_swap_fn.php
  76. * Plugin file with function for Hot swap
  77. * /
  78. function _myFn_2 ($ param)
  79. {
  80. $ _rand = rand (0, 99999999);
  81. $ _arr = get_defined_functions ();
  82. return sha1 ($ _ rand. $ param. implode ( ',' , $ _arr [ 'internal' ]));
  83. }
  84. return array (
  85. '_myFn' => array ( 'v' => 2,
  86. 'fn' => '_myFn_2' )
  87. );
  88. ?>
* This source code was highlighted with Source Code Highlighter .

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


All Articles