Running PHP & Mysqli queries in Parallel - php

I'm tying to extract data from thousands of premade sql files. I have a script that does what I need using the Mysqli driver in PHP, but it's really slow since it's one sql file at a time. I modified the script to create unique temp database names, which each sql file is loaded into. Data is extracted to an archive database table, then the temp database is dumped. In an effort to speed things up, I created a script structured 4 scripts similar to the one below, where each for loop is stored in it's own unique PHP file (the code below is only for a quick demo of what's going on in 4 separate files), they are setup to grab only 1/4 of the files from the source file folder. All of this works perfectly, the scripts run, there is zero interference with file handling. The issue is that I seem to get almost zero performance boost. Maybe 10 seconds faster :( I quickly refreshed my PHPmyadmin database listing page and could see the 4 different databases loaded at anytime, but I also noticed that it looked like it was still running more or less sequentially as the DB names were changing on the fly. I went the extra step of creating an unique user for each script with it's own connection. No improvement. Can I get this to work with mysqli / PHP or do I need to look into some other options? I'd prefer to do this all in PHP if I can (version 7.0). I tested by running the PHP scripts in my browser. Is that the issue? I haven't written any code to execute them on the command line and set them to the background yet. One last note, all the users in my mysql database have no limits on connections, etc.
$numbers = array('0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20');
$numCount = count($numbers);
$a = '0';
$b = '1';
$c = '2';
$d = '3';
$rebuild = array();
echo"<br>";
for($a; $a <= $numCount; $a+=4){
if(array_key_exists($a, $numbers)){
echo $numbers[$a]."<br>";
}
}
echo "<br>";
for($b; $b <= $numCount; $b+=4){
if(array_key_exists($b, $numbers)){
echo $numbers[$b]."<br>";
}
}
echo "<br>";
for($c; $c <= $numCount; $c+=4){
if(array_key_exists($c, $numbers)){
echo $numbers[$c]."<br>";
}
}
echo "<br>";
for($d; $d <= $numCount; $d+=4){
if(array_key_exists($d, $numbers)){
echo $numbers[$d]."<br>";
}
}

Try this:
<?php
class BackgroundTask extends Thread {
public $output;
protected $input;
public function run() {
/* Processing here, use $output for... well... outputting data */
// Here you would implement your for() loops, for example, using $this->input as their data
// Some dumb value to demonstrate
$output = "SOME DATA!";
}
function __construct($input_data) {
$this->input = $input_data;
}
}
// Create instances with different input data
// Each "quarter" will be a quarter of your data, as you're trying to do right now
$job1 = new BackgroundTask($first_quarter);
$job1->start();
$job2 = new BackgroundTask($second_quarter);
$job2->start();
$job3 = new BackgroundTask($third_quarter);
$job3->start();
$job4 = new BackgroundTask($fourth_quarter);
$job4->start();
// ==================
// "join" the first job, i.e. "wait until it's finished"
$job1->join();
echo "First output: " . $job1->output;
$job2->join();
echo "Second output: " . $job2->output;
$job3->join();
echo "Third output: " . $job3->output;
$job4->join();
echo "Fourth output: " . $job4->output;
?>
When using four calls to your own script through HTTP, you're overloading your connections for no useful reason. Instead, you're taking away spaces for other users who may be trying to access your website.

Related

What causes mod_security 406 Not Acceptable when POSTing data?

I have an article on my website with the (markdown) content:
# PHP Proper Class Name
Class names in PHP are case insensitve. If you have a class declaration like:
```php
class MyWeirdClass {}
```
you can instantiate it with `new myWEIRDclaSS()` or any other variation on the case. In some instances, you may want to know, what is the correct, case-sensitive class name.
### Example Use case
For example, in one of my libraries under construction [API Doccer](https://github.com/ReedOverflow/PHP-API-Doccer), I can view documentation for a class at url `/doc/class/My-Namespace-Clazzy/` and if you enter the wrong case, like `/doc/class/my-NAMESPACE-CLAzzy`, it should automatically redirect to the proper-cased class. To do this, I use the reflection method below as it is FAR more performant than the `get_delcared_classes` method
## Reflection - get proper case
Credit goes to [l00k on StackOverflow](https://stackoverflow.com/a/35222911/802469)
```php
$className = 'My\caseINAccuRATE\CLassNamE';
$reflection = new ReflectionClass($className);
echo $reflection->getName();
```
results in `My\CaseInaccurate\ClassName`;
Running the benchmark (see below) on localhost on my laptop, getting the proper case class name of 500 classes took about 0.015 seconds, as opposed to ~0.050 seconds using the `get_declared_classes` method below.
## get_declared_classes - get proper case
This was my idea, as I hadn't even considered using reflection, until I saw [l00k's answer on StackOverflow](https://stackoverflow.com/a/35222911/802469). Guessing it would be less efficient, I wrote the code and figured it out anyway, because it's fun!
```php
$wrongCaseName = 'Some\classy\THIng';
class_exists($wrongCaseName); //so it gets autoloaded if not already done
$classes = get_declared_classes();
$map = array_combine(array_map('strtolower',$classes),$classes);
$proper = $map[strtolower($wrongCaseName)];
```
results in `$proper = 'Some\Classy\Thing'`;
Running the bencmark (see below) on localhost on my laptop, getting the proper case class name of 500 classes took about 0.050 seconds, as opposed to ~0.015 seconds with reflection (above).
## Benchmark:
I used the following code to do the benchmark, removing the `classes` directory between each run of the benchmark. It's not perfect. At all. But it gets the job done well enough, I think:
```php
<?php
$times = [];
$times['begin'] = microtime(TRUE);
spl_autoload_register(function($className){
if (file_exists($name=__DIR__.'/classes/'.strtolower($className).'.php')){
include($name);
}
});
if (is_dir(__DIR__.'/classes'))return;
mkdir(__DIR__.'/classes');
function generateRandomString($length = 10) {
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
$times['start_file_write'] = microtime(TRUE);
$names = [];
for ($i=0;$i<500;$i++){
$className = generateRandomString(10);
$file = __DIR__.'/classes/'.strtolower($className).'.php';
if (file_exists($file)){
$i = $i-1;
continue;
}
$code = "<?php \n\n".'class '.$className.' {}'."\n\n ?>";
file_put_contents($file,$code);
$names[] = strtoupper($className);
}
$times['begin_get_declared_classes_benchmark'] = microtime(TRUE);
$propers = [];
// foreach($names as $index => $name){
// $wrongCaseName = strtoupper($name);
// class_exists($wrongCaseName); //so it gets autoloaded if not already done
// $classes = get_declared_classes();
// $map = array_combine(array_map('strtolower',$classes),$classes);
// $proper = $map[strtolower($wrongCaseName)];
// if ($index%20===0){
// $times['intermediate_bench_'.$index] = microtime(TRUE);
// }
// $propers[] = $proper;
// }
// the above commented lines are the get_declared_classes() method.
// the foreach below is for reflection.
foreach ($names as $index => $name){
$className = strtoupper($name);
$reflection = new ReflectionClass($className);
if ($index%20===0){
$times['intermediate_bench_'.$index] = microtime(TRUE);
}
$propers[] = $reflection->getName();
}
$times['end_get_declared_classes_benchmark'] = microtime(TRUE);
$start = $times['begin'];
$bench = $times['begin_get_declared_classes_benchmark'];
$lastTime = 0;
foreach($times as $key => $time){
echo "\nTime since begin:".($time-$start);
echo "\nTime since last: ".($time-$lastTime)." key was {$key}";
echo "\nTime since bench start: ".($time - $bench);
$lastTime = $time;
}
print_r($times);
print_r($propers);
exit;
```
### Results
```
// get_declared_classes method
//Time since bench start: 0.052499055862427 is total time for processing get_declared_classes w/ $i=500
//Time since bench start: 0.047168016433716
// last bench time Time since begin:0.062150955200195
// 100 intermediate bench: Time since bench start: 0.0063230991363525
// 200 : Time since bench start: 0.015070915222168
// 300 intermediate bench: Time since bench start: 0.02455997467041
// 400 intermediate bench: Time since bench start: 0.033944129943848
// 480 : Time since bench start: 0.044310092926025
//reflection method:
//Time since bench start: 0.01493501663208
//Time since bench start: 0.017416954040527
// 100 intermediate: Time since bench start: 0.0035450458526611
// 200 intermediate: Time since bench start: 0.0066778659820557
// 300 intermediate: Time since bench start: 0.010055065155029
// 400 intermediate: Time since bench start: 0.014182090759277
// 480 intermediate: Time since bench start: 0.01679801940918
```
#### Results' notes
- "Time since bench start" is the entire time it took to run all the iterations. I share this twice above.
- "100 Intermediate" (200, 300, etc) are actually the results at 120, 220, etc... I messed up in copy+pasting results & didn't want to do it again. Yes. I'm lazy :)
- The results would of course vary between runs of the code, but it's pretty clear that the reflection option is significantly faster.
- All was run on a localhost server on an Acer laptop.
- PHP Version 7.2.19-0ubuntu0.19.04.1 (from `php info()`)
As shown above, I'm able to submit the article & everything works as expected - saves to DB & everything. The very last line, if I change php info() to phpinfo() (removing the space), I get this error:
Not Acceptable!
An appropriate representation of the requested resource could not be found on this server. This error was generated by Mod_Security.
When I try to submit with phpinfo() (no space), my PHP does not execute at all and I only get this error. The network tab in firefox shows "406 Not Acceptable" for the status code. Nothing is being written to my error log in $_SERVER['DOCUMENT_ROOT'].'/error_log', which is where all the PHP errors log to, anyway. In my home folder, there is a logs folder, but it remains empty. No logs in /etc/ or /etc/my_website_name.com either.
What could be causing this problem? Is there something in the PHP.ini I could change? Could .htaccess affect this at all?
At the very least, how do I troubleshoot this problem?
Troubleshooting
I can submit an article which only contains - PHP Version 7.2.19-0ubuntu0.19.04.1 (from `phpinfo()`) in the body.
If I remove phpinfo() and add more content to the body of the post (more total data being submitted), it works
putting a space, like php info() makes it work, and is how the post currently exists.
I don't know what else to do
I am using Simple MDE now, but it happened on multiple other occasions before I started using Simple MDE. It has only been with relatively large posts that also contain code.
I am on Shared Hosting with HostGator, using HTTPS:// and PHP 7.2.19
I contacted HostGator. They added something to a white list, but didn't give me intimate details. It fixed the problem.
First agent took awhile, failed to resolve the issue, and disconnected prematurely.
The second agent was reasonably prompt & resolved the problem, saying I shouldn't have this issue with similar types of POST requests which contain code.

PHP executing two C programs with proc_open

I've created two programs in C. The first gets a number and prints the double value of it and the second prints the quadruple. I want to execute them
through PHP. I've done it using proc_open and it works fine if I execute only one of the programs each time. I have to give a number to the
first program and pass its output as input to the second program. When though I use two proc_open to create the two processes,the whole thing doesn't work.
What I want to do is something like this:
$process1 = proc_open($command_exe1, $descriptorspec1, $pipes1, $cwd);
$process2 = proc_open($command_exe2, $descriptorspec2, $pipes2, $cwd);
fwrite($pipes1[0], $posted);
fwrite($pipes2[0], $pipes1[1]);
fclose($pipes1[0]);
fclose($pipes2[0]);
while(!feof($pipes1[1])) {
$StdOut1 = stream_get_contents($pipes1[1]);
}
echo $StdOut1;
while(!feof($pipes2[1])) {
$StdOut2 = stream_get_contents($pipes2[1]);
}
echo $StdOut2;
fclose($pipes1[1]);
fclose($pipes2[1]);
proc_close($process1);
proc_close($process2);
I know that it's a wrong way of doing it but I can't think of anything else so...any help would be welcome.
Note: I'm working on Windows.
If process can run separately one after another
You can try put "in-steps",
/** step 1*/
$process1 = proc_open($command_exe1, $descriptorspec1, $pipes1, $cwd)
...
while(!feof($pipes1[1])) {
$StdOut1 = stream_get_contents($pipes1[1]);
}
echo $StdOut1;
/** step 2*/
$process2 = proc_open($command_exe2 $descriptorspec2, $pipes2, $cwd)
while(!feof($pipes2[1])) {
...

determining proper gearman task function to retrieve real-time job status

Very simply, I have a program that needs to perform a large process (anywhere from 5 seconds to several minutes) and I don't want to make my page wait for the process to finish to load.
I understand that I need to run this gearman job as a background process but I'm struggling to identify the proper solution to get real-time status updates as to when the worker actually finishes the process. I've used the following code snippet from the PHP examples:
do {
sleep(3);
$stat = $gmclient->jobStatus($job_handle);
if (!$stat[0]) // the job is known so it is not done
$done = true;
echo "Running: " . ($stat[1] ? "true" : "false") . ", numerator: " . $stat[2] . ", denomintor: " . $stat[3] . "\n";
} while(!$done);
echo "done!\n";
and this works, however it appears that it just returns data to the client when the worker finished telling the job what to do. Instead I want to know when the literal process of the job finished.
My real-life example:
Pull several data feeds from an API (some feeds take longer than others)
Load a couple of the ones that always load fast, place a "Waiting/Loading" animation on the section that was sent off to a worker queue
When the work is done and the results have been completely retrieved, replace the animation with the results
This is a bit late, but I stumbled across this question looking for the same answer. I was able to get a solution together, so maybe it will help someone else.
For starters, refer to the documentation on GearmanClient::jobStatus. This will be called from the client, and the function accepts a single argument: $job_handle. You retrieve this handle when you dispatch the request:
$client = new GearmanClient( );
$client->addServer( '127.0.0.1', 4730 );
$handle = $client->doBackground( 'serviceRequest', $data );
Later on, you can retrieve the status by calling the jobStatus function on the same $client object:
$status = $client->jobStatus( $handle );
This is only meaningful, though, if you actually change the status from within your worker with the sendStatus method:
$worker = new GearmanWorker( );
$worker->addFunction( 'serviceRequest', function( $job ) {
$max = 10;
// Set initial status - numerator / denominator
$job->sendStatus( 0, $max );
for( $i = 1; $i <= $max; $i++ ) {
sleep( 2 ); // Simulate a long running task
$job->sendStatus( $i, $max );
}
return GEARMAN_SUCCESS;
} );
while( $worker->work( ) ) {
$worker->wait( );
}
In versions of Gearman prior to 0.5, you would use the GearmanJob::status method to set the status of a job. Versions 0.6 to current (1.1) use the methods above.
See also this question: Problem With Gearman Job Status

PHP and Windows WMI, CPU Temperature and false readings?

I wrote this PHP function:
<?php
//windows cpu temperature
function win_cpu_temp(){
$wmi = new COM("winmgmts://./root\WMI");
$cpus = $wmi->execquery("SELECT * FROM MSAcpi_ThermalZoneTemperature");
foreach ($cpus as $cpu) {
$cpupre = $cpu->CurrentTemperature;
}
$cpu_temp = ($cpupre/10)-273.15 . ' C';
return $cpu_temp;
}
echo win_cpu_temp();
?>
My problem, is that the script displays 59.55 C which I had thought was correct. I checked this value several hours later, and it's exactly the same. I just put the CPU to work at 90% compressing video for ten minutes, and this value is the same still.
Can anyone help me find the "true" value for this function?
I've read (to no avail):
MSAcpi_ThermalZoneTemperature class not showing actual temperature
How is, say, "Core Temp" getting its values? Same computer, it reports between 49 and 53 Celsius.
With a little digging around I found the common issue with using MSAcpi_ThermalZoneTemperature was that it is dependent upon being implemented on your system.
You could try querying Win32_TemperatureProbe and see if you have any luck there.
Neither MSAcpi_ThermalZoneTemperature or Win32_TemperatureProbe worked on my system, although if you have admin access, you can use http://openhardwaremonitor.org/ which provides a WMI interface for all available sensor data.
This worked great for me and I was able to accurately report CPU Core temp from a PHP script:
function report_cpu_temp(){
$wmi = new COM('winmgmts://./root/OpenHardwareMonitor');
$result = $wmi->ExecQuery("SELECT * FROM Sensor");
foreach($result as $obj){
if($obj->SensorType == 'Temperature' && strpos($obj->Parent, 'cpu') > 0)
echo "$obj->Name ($obj->Value C)"; // output cpu core temp
else
echo 'skipping ' . $obj->Identifier ;
echo '<br />';
}
}
Hope this helps.

Executing MySQL Queries and Creating Instances several Times within Loop

I have an Web Application that requires SELECT and INSERT Querying to MySQL database and Instantiating a PHP class using new operator almost more that thousand times within a loop. May be there are alternatives to my present logic, but my point is that is there any harm if I carry on this logic?. I don't bother about the time complexity associated with the algorithm presently but **worrying much about if anything goes wrong during transaction or memory usage. I am giving the piece of code for reference
$stm_const = "select ce.TIMETAKEN, qm.QMATTER as STRING1, ce.SMATTER as STRING2 from w_clkexam ce, clkmst cm, qsmst qm where ce.QID=qm.QID and cm.ROLLNO=ce.ROLLNO";
for ($c=0; $c < count($rollnos); $c++) {
$stm3 =$stm_const." "."and ce.ROLLNO='$rollnos[$c]'";
$qry3 = mysql_query($stm3) or die("ERROR 3:".mysql_error());
while($row1 = mysql_fetch_array($qry3)) {
echo $string1=$row1['STRING1'];
echo $string2=$row1['STRING2'];
$phpCompareStrings=new PhpCompareStrings($string2, $string1);
$percent=$phpCompareStrings->getSimilarityPercentage();
$percent2=$phpCompareStrings->getDifferencePercentage();
echo '$string1 and $string2 are '.$percent.'% similar and '.$percent2.'% differnt<br/>';
}// end while
}// end for
Please help, I am waiting for opinions from you so that I can move further. Thanks in advance.
I don't see any problem there. You just get all the rows from database and for each row compare the strings. As you assign the object to the same variable each time, the old object is destroyed before the new object is created. So you have only one instance of the object in memory at all times. The question is what you want to do with the results? Only print them as in your example, or to store the results for further processing?
Anyway, I think it is not possible to optimize your code without modifying the class. If you are using this class, you can try to modify it so that it can accept multiple strings. Using this, you can create only 1 instance of the class and you avoid destroying/creating the object for every row. It will save you some CPU time, but not memory (as at all times, only 1 instance of the class is active).
Untested modification below:
Modify this function inside the class:
function __construct($str1,$str2){
$str1=trim($str1);
$str2=trim($str2);
if($str1==""){ trigger_error("First parameter can not be left blank", E_USER_ERROR); }
elseif($str2==""){ trigger_error("Second parameter can not be left blank", E_USER_ERROR); }
else{
$this->str1=$str1;
$this->str2=$str2;
$this->arr1=explode(" ",$str1);
$this->arr2=explode(" ",$str2);
}
}
To these 2 functions:
function init($str1,$str2){
$str1=trim($str1);
$str2=trim($str2);
if($str1==""){ trigger_error("First parameter can not be left blank", E_USER_ERROR); }
elseif($str2==""){ trigger_error("Second parameter can not be left blank", E_USER_ERROR); }
else{
$this->str1=$str1;
$this->str2=$str2;
$this->arr1=explode(" ",$str1);
$this->arr2=explode(" ",$str2);
}
}
function __construct($str1,$str2){ $this->init($str1,$str2); }
Then create the object outside the loop and only call $phpCompareStrings->init($string2,$string1) inside the loop.

Categories