My php project is now working on Apache with mod_php. I want to open OpCache to accelerate my code. But I'm worry about if it's safe to update the code.
The validate_timestamps should be 0. Because the code may expire when you update new version, and this can make the code of new version call the code of old version.
But even if I set validate_timestamps=0, this can also happen. When I update the code file, one of the code file that hasn't been cached is called. And thus this code file is in new version while the caller is in old version. Maybe I can load all the code files into cache before the server start?
If I find a way to make sure all the files in the project dir are loaded into cache before the Apache start to serve requests, I can update new version code first, and then clear all the OpCache. But does it have an impact on those working requests? Will they run an old logic and then be switch into a new one?
Besides, how to clear all the OpCache in an atom time? If it takes time to clear all the OpCache, the problem is still there, right?
Can anyone help me? Thank you very much!
Let me show an example.
I have two files on my directory, and the Apache's working directory is also there.
The entry file is index.php.
<?php
echo "verson 1: index";
// do some work
sleep(1);
// not all APIs will call this library
// I just use rand to represent a probability
if (rand(0,100) == 1) {
require_once BASEPATH.'lib.php';
t = new Tester();
t->func1();
}
And the other php file is lib.php.
<?php
echo "---- version 1: lib";
class Tester {
function __construct() {
echo "---- new Tester in lib.php";
}
public function func1() {
echo "---- Tester.func1()";
}
}
Now I'm going to upload my new version code.
In the new version, I declare a new function func2() in Tester and delete the old function func1(). I call it in the index.php.
index.php
<?php
echo "version 2: index";
// do some work
sleep(1);
// not all APIs will call this library
// I just use rand to represent a probability
if (rand(0,100) == 1) {
require_once BASEPATH.'lib.php';
t = new Tester();
t->func2();
}
lib.php
<?php
echo "---- version 2: lib";
class Tester {
function __construct() {
echo "---- new Tester in lib.php";
}
public function func2() {
echo "---- Tester.func2()";
}
}
I close the OpCache. I upload the index.php first and then the other one lib.php. At the moment between I finish to upload the index.php and begin to upload lib.php, a request is running into the "rand case". Then I will get an error about "func2() not defined", because the lib.php is still the old one at that time.
If I open the OpCache, it's the same if I don't set validate_timestamps to 0.
After I set validate_timestamps to 0, the opcode will not expire. So I must call opcache_reset manually after I finish to upload index.php and lib.php. At that moment, a request also runs into the "rand case". If the opcache_reset operation takes a period of time, and it doesn't block the request running, the error "func2() not defined" may also occur.
How to make all the requests safe when I upgrade my code version?
Related
The very simple PHP code below works perfectly, I mean, it does nothing and at the same time it does not use almost any system memory.
<?php
set_time_limit(0);
ini_set('memory_limit','-1');
for ($i=0;$i<100000000;$i++) {
$bbb = function() {
return true;
};
unset($bbb);
}
?>
While the code below is very similar BUT if you run it, after a few minutes you will crash your system because it will consume all your system RAM.
<?php
set_time_limit(0);
ini_set('memory_limit','-1');
for ($i=0;$i<100000000;$i++) {
eval('
$bbb = function() {
return true;
};
');
unset($bbb);
}
?>
My actual $bbb function is much more complex and it is a result of my AI output, so I really need to use eval in this case. I am totally aware of security implications.
I just wanna know how to solve this problem. How can I make PHP/APACHE release memory (the GC probably is the curlprit) in the second case?
NOTE: I am using Windows 10 with PHP 7.4
EDIT
As per suggestion of #Nigel Ren (which looks very good) I tried this:
<?php
set_time_limit(0);
ini_set('memory_limit','-1');
for ($i=0;$i<100000000;$i++) {
require("test.php");
unset($bbb);
}
?>
And the "test.php" file has this:
<?php
$bbb = function() {
return true;
};
?>
But this suggestion still consumes lots of memory, it's not getting cleared! Even if I use gc_mem_caches(); GC is still not clearing memory. Maybe I understood something wrong from your suggestion ##Nigel Ren?
See the last comment by Chris on this bug report.
For posterity's sake I'll copy it here:
I became interested by the eval() behavior recently, and I can confirm that the memory usage gets higher by the time of the execution.
However it is not really a bug, but the way eval() is fundamentally written:
it creates a temp file with the code inside (sometimes it is stored in memory)
it then includes that temp file using a normal include() function
As you can see, the more eval() you do, the more include() it will trigger, resulting in a memory usage that cannot be freed...
So the reason your memory usages increases when using eval is that it's including temporary files. There's nu way to un-include files, so the more you use eval, the more memory you're going to end up using.
Disclaimer: the following is pure speculation, I have not verified if this will work
As for a potential workaround to get around this issue, since you say you need to use eval, you could look into using the pcntl extension to fork a process.
It's probably going to be slower, but if you can fork the eval-ing piece of your code off to a separate process which can then terminate gracefully once finished, the "include" performed in that child process should be cleared up. That might be one way to limit the memory usage in your application.
After lots of reseraching I discovered runkit7 is the way to go! It allows clearing RAM even from functions included/required/evaluated! You can use include/require/eval and it will wisely clean the memory footprint. It's a pretty awesome extension and I was amazed as soon as I tested it, my ram dropped 90% from the previous run I did without runkit7.
As a test, this code starts with a template for the source code in your first listing minus the definition but with a place holder to insert the code. The script just reads this file, replaces the marker with the AI script and writes a new file (abc1.php), then uses require to run the script. This removes any need for eval and the overheads associated with it...
So abc.php is
set_time_limit(0);
ini_set('memory_limit','-1');
for ($i=0;$i<10;$i++) {
//#
unset($bbb);
}
The bootstrap script is, where $aiSrc is the code you generate (note this also runs the function)...
$src = file_get_contents("abc.php");
$aiSrc = '$bbb = function() {
echo "Hello";
};
$bbb();';
$newSrc = str_replace("//#", $aiSrc, $src);
file_put_contents("abc1.php", $newSrc);
require"abc1.php";
After this the script abc1.php contains...
set_time_limit(0);
ini_set('memory_limit','-1');
for ($i=0;$i<10;$i++) {
$bbb = function() {
echo "Hello";
};
$bbb();
unset($bbb);
}
I am using CodeIgniter framework with RabbitMQ for doing time consuming activities such as sending emails, generating PDFs.
Whenever I change a code in the PHP files, say library or controller, until I restart the Apache, the old code is being executed by the RabbitMQ. How to resolve this?
The function in Controller,
public function receive()
{
$this->db->reconnect();
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('generate_pdf', false, false, false, false);
echo "[*] Started listening to connections... To exit press CTRL+C\n";
$generate_pdf = function ($data)
{
$this->generatepdf->generate($data); // Generatepdf.php library -> generate() function
};
$channel->basic_qos(null, 1, null);
$channel->basic_consume('generate_pdf', '', false, true, false, false, $generate_pdf)
while ($channel->is_consuming())
{
$channel->wait();
}
}
Starting this in termimal,
php index.php Controllerfile.php receive
This works fine as expected. So when a job is pushed to the queue, the generate() function in the library file Generatepdf.php is called and the execution is done.
However, if I modify the generate() function thru an editor or I re-upload the file with changes, the changes does not reflect until I restart the Apache server.
Function definition before changes:
public function generate()
{
echo "Hello the data is 250";
}
Output: Hello the data is 250
Function definition after the change:
public function generate()
{
echo "Hello the data is 251";
}
Output before restarting Apache: Hello the data is 250
Output after restarting Apache: Hello the data is 251
Why does this happen? How can I made the execution to refresh the changes in the file?
The code you've shown contains this loop:
while ($channel->is_consuming())
{
$channel->wait();
}
What that's saying is "unless the connection to RabbitMQ breaks, wait for a new message, process it, and then wait again, forever". In other words, it's an infinite loop, and the PHP process running it won't exit unless you kill it.
Contrary to some descriptions I've seen, PHP is a compiled language, not an interpreted one: when a PHP process is asked to load a file of PHP code, it looks at it once, compiles it to a machine-friendly representation (called "op codes") and then doesn't need to look at it again to execute the code in it any number of times. So when you edit the file, a process that's already running won't notice, and won't compile your new code.
If you want to have the new code loaded, you need to exit the infinite loop, so the process ends, and then start a new one, which will compile your new code. For instance, you can have a check in the loop for a particular file, measure the time it's been running, or listen for a special message telling it to exit. You can then use something like "supervisord" to ensure the script is re-run whenever it exits, or just have a "cron" task to regularly start a new copy.
As one extra point: there is a feature of PHP called "OpCache" which reuses the compiled version of files even between processes, for better performance. By default, it will look at the timestamp of the file to decide whether to recompile it, so will "notice" if you edit a file, but that check can be turned off, for maximum performance. If that was the problem, you'd see this with all PHP code, though, not just your RabbitMQ loop.
I'm using MAMP with PHP 5.4.10 and I have a problem with the following MWE:
<?php
trait T {
public function hello() { echo 'hello'; }
}
class A {
use T;
}
$a = new A();
$a->hello();
?>
The page shows 'hello' on the first load. But then, when I hit refresh, I get an Error 500.
If I modify the file (just by adding an empty line somewhere for instance) and refresh again, 'hello' shows up again. Hit refresh again, and the Error 500 is back.
Any clue where this might be coming from?
Update:
This shows up in the PHP errors log (nothing in the Apache errors log): PHP Fatal error: Call to undefined method A::0?
()
(the 0 doesn't always have the same name when I repeat the operation).
Xcache might be the problem here, try turning caching off (or at least xcache) and try it again
I had the same problem, and thanks to the #Leon Weemen i focused on the XCache. I found this bug (which is fixed in XCache 3.0.1) to be exactly what causes the problem (my version of XCache was 2.0.0). They suggest you to set in your php.ini the following values to solve the problem;
xcache.mmap_path = "/tmp/xcache"
xcache.readonly_protection = on
However, this workaround does not solve the problem for me. The only way I was able to disable the XCache was by using the ini_set() PHP method. The following snippet at the very begginning of my application solves the problem and is ready to use XCache as soon as it is updated:
try{
$xCache = new ReflectionExtension('xcache');
if(version_compare($xCache->getVersion(), '3.0.1', '<')){
ini_set('xcache.cacher', 0);
}
} catch(ReflectionException $e){
// xCache not installed - everything should work fine
}
There is a vbscript that we must run to consolidate information gathered in a custom web application into our management software. The .vbs is in the same folder as the web application which is built in CodeIgniter 2.
Here is the controller code:
public function saveToPM( $budgetType ){
// run it
$obj = new COM( 'WScript.Shell' );
if ( is_object ( $obj ) ) {
$obj->Run( 'cmd /C wscript.exe D:\pamtest\myload.vbs', 0, true );
var_dump($obj->Run);
} else {
echo 'can not create wshell object';
} // end if
$obj = null;
//$this->load->view('goodPush');
} // end saveToPM function
We have enabled DCon in the php.ini file and used dcomcnfg to enable permissions for the user.
I borrowed the code from http://www.sitepoint.com/forums/showthread.php?505709-run-a-vbs-from-php.
The screen echos "Code executed" but the vbscript does not run.
We have been fighting with this for a while so any help is GREATLY appreciated.
It's a bit messy. PHP calls WScript.Shell.Run which will call cmd (with /c - i.e terminate cmd.exe when it's done its thing) which will call cscript.exe to run and interpret a .vbs. As you can see quite a few things that have to go right! :)
What if you 'wait' for the WScript.Shell.Run call to end (your $wait variable) before continuing execution of the wsh script which will in turn allow PHP to continue execution etc?
Since you're not waiting for the call to finish, PHP thinks its all good and continues onto the next line (interpreted language).
Also, maybe have the .vbs create an empty text file? Just so you have an indication that it has actually run.
Just take a step back, have a beer and it'll come to you! Gogo troubleshoot!
And - http://ss64.com/vb/run.html
If bWaitOnReturn is set to TRUE, the Run method returns any error code returned by the application.
I've tested your code with a stand-alone PHP script (without Codeigniter) on a Windows XP machine, with the PHP 5.4.4 built-in web server, and I've noticed that the vbscript gets executed, but PHP dies (the event viewer shows a generic "Application Error" with ID 1000).
However I've also discovered that removing the "cmd /C" from the command string solves the problem.
Here is the simple script that I've used for my test:
<?php
$obj = new COM('WScript.Shell');
if (is_object($obj)) {
//$obj->Run('cmd /C wscript.exe test.vbs', 0, true); // This does'nt work
$obj->Run('wscript.exe test.vbs', 0, true); // This works
var_dump($obj->Run);
} else {
echo 'can not create wshell object';
}
$obj = null;
?>
And this is my simple "test.vbs" script:
WScript.Echo "vbscript is running"
Another solution that seems to be working (at least on my platform) is the "system" call:
system('wscript.exe test.vbs');
Unfortunately I don't have a 64-bit IIS system to test with, so I can't really say if there are specific problems on this platform, but I hope this helps.
I have a JavaScript functions which calls a PHP function through AJAX.
The PHP function has a set_time_limit(0) for its purposes.
Is there any way to stop that function when I want, for example with an HTML button event?
I want to explain better the situation:
I have a php file which uses a stream_copy_to_stream($src, $dest) php function to retrieve a stream in my local network. The function has to work until I want: I can stop it at the end of the stream or when I want. So I can use a button to start and a button to stop. The problem is the new instance created by the ajax call, in fact I can't work on it because it is not the function that is recording but it is another instance. I tried MireSVK's suggest but it doesn't worked!
Depending on the function. If it is a while loop checking for certain condition every time, then you could add a condition that is modifiable from outside the script (e.g. make it check for a file, and create / delete that file as required)
It looks like a bad idea, however. Why you want to do it?
var running = true;
function doSomething(){
//do something........
}
setInterval(function(){if(running){doSomething()}},2000); ///this runs do something every 2 seconds
on button click simply set running = false;
Your code looks like:
set_time_limit(0);
while(true==true){//infinite loop
doSomething(); //your code
}
Let's upgrade it
set_time_limit(0);
session_start();
$_SESSION['do_a_loop'] = true;
function should_i_stop_loop(){
#session_start();
if( $_SESSION['do_a_loop'] == false ) {
//let's stop a loop
exit();
}
session_write_close();
}
while(true==true){
doSomething();
should_i_stop_loop(); //your new function
}
Create new file stopit.php
session_start();
$_SESSION['do_a_loop'] = false;
All you have to do now is create a request on stopit.php file (with ajax or something)
Edit code according to your needs, this is point. One of many solutions.
Sorry for my English
Sadly this isn't possible (sort of).
Each time you make an AJAX call to a PHP script the script spawns a new instance of itself. Thus anything you send to it will be sent to a new operation, not the operation you had previously started.
There are a number of workarounds.
Use readystate 3 in AJAX to create a non closing connection to the PHP script, however that isn't supported cross browser and probably won't work in IE (not sure about IE 10).
Look into socket programming in PHP, which allows you to create a script with one instance that you can connect to multiple times.
Have PHP check a third party. I.E have one script running in a loop checking a file or a database, then connect to another script to modify that file or database. The original script can be remotely controlled by what you write to the file/database.
Try another programming language (this is a silly option, but I'm a fan of node). Node.js does this sort of thing very very easily.