Loop Making PHP output and browser-viewed source code vanish - php

I recently started coding a script meant for html output that (hopefully) will reduce some busy work at my job. It is a script adapted from a functional script I wrote in python, but I do not think that is the issue.
I have the following code at the start of the php script to identify errors:
ini_set('display_errors', 1);
error_reporting(E_ALL ^ E_NOTICE);
I have put the program on a server and attempted to run it. My first problem is that partway through the program, it starts dumping the source code (minus HTML tags) to the window partway through an if statement as such:
$moonSet[0]){
$t = 3;
} # Case 4, set time greater than rise time
else {
$t = 4;
}
For reference, the actual section of code interrupted is as such:
# Case 1, no rise time on current date
if ($moonRise[0] == 0){
$t = 1;
} # Case 2, no set time on current/next date
elseif ($moonSet[0] == 0){
$t = 2;
} # Case 3, rise time greater than set time
elseif ($moonRise[0] > $moonSet[0]){
$t = 3;
} # Case 4, set time greater than rise time
else {
$t = 4;
}
I believe the version of php used on the server is at least PHP 5.x, as the source code is dumped to the window when the html coding uses <??> instead of <?php?>. The problem is that when I use <?php?> all the output vanishes--the HTML tags included. Looking at the source code, it only shows a 1 to indicate the first line of code, suggesting that no code is actually in the file. This only happens when I use <?php?> instead of <??>.
I then tried to gradually step through the code to identify why, one, the code suddenly interrupts the if statement, and two, why using <?php?> torches the output.
What I ended up discovering is that the <?php?> code works just fine until I added in a loop. The program uses a MySQL query, and from what I can tell, the query worked just fine. The program executed, output at both the beginning and end of the test code was displayed to screen.
The SQL code is as such:
$connection = mysql_connect("localhost", "webuser", "webuser");
mysql_select_db("dbAstro", $connection);
$queryTides = "SELECT tideDateTime, tideHeight, tideHorL FROM tblTides WHERE DATE(tideDateTime) BETWEEN '$targetDate[0] 00:00:00' AND '$targetDate[2] 23:59:59' ORDER BY tideDateTime";
$querySun = "SELECT sunDate, sunRise, sunSet FROM tblSunRiseSet WHERE DATE(sunDate) BETWEEN '$targetDate[0] 00:00:00' AND '$targetDate[2] 23:59:59' ORDER BY sunDate";
$queryMoon = "SELECT moonDate, moonRise, moonSet FROM tblMoonRiseSet WHERE DATE(moonDate) BETWEEN '$targetDate[0] 00:00:00' AND '$targetDate[2] 23:59:59' ORDER BY moonDate";
$queryTwi = "SELECT twilightDate, twilightBeg, twilightEnd FROM tblCivilTwilight WHERE DATE(twilightDate) BETWEEN '$targetDate[0] 00:00:00' AND '$targetDate[2] 23:59:59' ORDER BY twilightDate";
#Query results stored in separate result locations for later reference.
$resultTi = mysql_query($queryTides, $connection);
$resultSu = mysql_query($querySun, $connection);
$resultMo = mysql_query($queryMoon, $conneciton);
$resultTwi = mysql_query($queryTwi, $connection);
The difference between using the <?php?> and <??> tags so far is that <??> will not display a printf('') command. <?php?> displayed the printf('') command, but with a message that a null value is in the database--which there are some null values in the database.
The problem is when the script enters a loop. Upon putting a loop command, either while or for, the <?php?> script vanishes from both the output and when viewing the source code in a browser. <??> as a tag still outputs the HTML, but does not output any of the php commands--a basic printf('') or an echo command.
I want to stress that it does this with any loop--not just a while loop that searched the SQL queries (the intended goal, shown abbreviated below):
$b = 0;
while ($astroRow = mysql_fetch_assoc($resultTi)) {
$tideTime[$b] = date_format("$astroRow[tideDateTime]", "hi");
$b++;
}
When I attempted to replace the loop with something extremely basic, like this:
for ($a = 0; $a < 2; $a++) {
echo $a;
}
the same pattern occurred. One would start dumping output to screen or show no output from php, and the <?php?> tags would show no code or output whatsoever.
I am at a loss. I do not know what the problem is, and attempting to search help sites has not led me to an answer to my problem. Would anyone have an idea what exactly is going on?

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 SQLite - prepared statement misbehaves?

I have the following SQLite table
CREATE TABLE keywords
(
id INTEGER PRIMARY KEY,
lang INTEGER NOT NULL,
kwd TEXT NOT NULL,
count INTEGER NOT NULL DEFAULT 0,
locs TEXT NOT NULL DEFAULT '{}'
);
CREATE UNIQUE INDEX kwd ON keywords(lang,kwd);
Working in PHP I typically need to insert keywords in this table, or update the row count if the keyword already exists. Take an example
$langs = array(0,1,2,3,4,5);
$kwds = array('noel,canard,foie gras','','','','','');
I now these data run through the following code
$len = count($langs);
$klen = count($kwds);
$klen = ($klen < $len)?$klen:$len;
$sqlite = new SQLite3('/path/to/keywords.sqlite');
$iStmt = $sqlite->prepare("INSERT OR IGNORE INTO keywords (lang,kwd)
VALUES(:lang,:kwd)");
$sStmt = $sqlite->prepare("SELECT rowid FROM keywords WHERE lang = :lang
AND kwd = :kwd");
if (!$iStmt || !$sStmt) return;
for($i=0;$i < $klen;$i++)
{
$keywords = $kwds[$i];
if (0 === strlen($keywords)) continue;
$lang = intval($langs[$i]);
$keywords = explode(',',$keywords);
for($j=0;$j < count($keywords);$j++)
{
$keyword = $keywords[$j];
if (0 === strlen($keyword)) continue;
$iStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
$iStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
$sStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
$sStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
trigger_error($keyword);
$iStmt->execute();
$sqlite->exec("UPDATE keywords SET count = count + 1 WHERE lang =
'{$lang}' AND kwd = '{$keyword}';");
$rslt = $sStmt->execute();
trigger_error($sqlite->lastErrorMsg());
trigger_error(json_encode($rslt->fetchArray()));
}
}
which generates the following trigger_error output
Keyword: noel
Last error: not an error
SELECT Result: {"0":1,"id":1}
Keyword: canard
Last Error: not an error
SELECT Reult:false
Keyword:foiegras
Last Error: not an error
SELECT Result: false
From the SQLite command line I see that the three row entries are present and correct in the table with the id/rowid columns set to 1, 2 and 3 respectively. lastErrorMsg does not report an error and yet two of the three $rslt->fetchArray() statements are returning false as opposed to an array with rowid/id attributes. So what am I doing wrong here?
I investigated this a bit more and found the underlying case. In my original code the result from the first SQLite3::execute - $iStmt-execute() - was not being assigned to anything. I did not see any particular reason for fetching and interpreting that result. When I changed that line of code to read $rslt = $iStmt->execute() I got the expected result - the rowid/id of the three rows that get inserted was correctly reported.
It is as though internally the PHP SQLite3 extension buffers the result from SQLiteStatement::execute function calls. When I was skipping the assignment my next effort at running such a statement, $sStmt->execute() was in effect fetching the previous result. This is my interpretation without knowing the inner workings of the PHP SQLite3 extension. Perhaps someone who understands the extension better would like to comment.
Add $rslt = NONE; right after trigger_error(json_encode($rslt->fetchArray())); and the correct results appear.
FetchArray can only be called once and somehow php is not detecting that the variable has changed. I also played with changing bindValue to bindParam and moving that before the loop but that is unrelated to the main issue.
It is my opinion that my solution should not work unless there is a bug in php. I am too new at the language to feel confident in that opinion and would like help verifying it. Okay, not a bug, but a violation of the least surprise principle. The object still exists in memory so without finalizing it or resetting the variable, fetch array isn't triggering.

Running PHP & Mysqli queries in Parallel

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.

How do I validate a PHP integer within a variable?

I have integrated Yelp reviews into my directory site with each venue that has a Yelp ID returning the number of reviews and overall score.
Following a successful MySQL query for all venue details, I output the results of the database formatted for the user. The Yelp element is:
while ($searchresults = mysql_fetch_array($sql_result)) {
if ($yelpID = $searchresults['yelpID']) {
require('yelp.php');
if ( $numreviews > 0 ) {
$yelp = '<img src="'.$ratingimg.'" border="0" /> Read '.$numreviews.' reviews on <img src="graphics/yelp_logo_50x25.png" border="0" /><br />';
} else {
$yelp = '';
}
} //END if ($yelpID = $searchresults['yelpID']) {
} //END while ($searchresults = mysql_fetch_array($sql_result)) {
The yelp.php file returns:
$yrating = $result->rating;
$numreviews = $result->review_count;
$ratingimg = $result->rating_img_url;
$url = $result->url;
If a venue has a Yelp ID and one or more reviews then the output displays correctly, but if the venue has no Yelp ID or zero reviews then it displays the Yelp review number of the previous venue.
I've checked the $numreviews variable type and it's an integer.
So far I've tried multiple variations of the "if ( $numreviews > 0 )" statement such as testing it against >=1, !$numreviews etc., also converting the integer to a string and comparing it against other strings.
There are no errors and printing all of the variables returned gives the correct number of reviews for each property with venues having no ID or no reviews returning nothing (as opposed to zero). I've also compared it directly against $result->review_count with the same problem.
Is there a better way to make the comparison or better format of variable to work with to get the correct result?
EDIT:
The statement if ($yelpID = $searchresults['yelpID']) { is not operating as it should. It is identical to other statements in the file, validating row contents which work correctly for their given variable, e.g. $fbID = $searchresults['fbID'] etc.
When I changed require('yelp.php'); to require_once('yelp.php'); all of the venue outputs changed to showing only the first iterated result. Looking through the venues outputted, the error occurs on the first venue after a successful result which makes me think there is a pervasive piece of code in the yelp.php file, causing if ($yelpID = $searchresults['yelpID']) { to be ignored until a positive result is found (a yelpID in the db), i.e. each venue is correctly displayed with a yelp number of reviews until a blank venue is encountered. The preceding venues' number of reviews is then displayed and this continues for each blank venue until a venue is found with a yelpID when it shows the correct number again. The error reoccurs on the next venue output with no yelpID and so on.
Sample erroneous output: (line 1 is var_dump)
string(23) "bayview-hotel-bushmills"
Bayview Hotel
Read 3 reviews on yelp
Benedicts
Read 3 reviews on yelp (note no var_dump output, this link contains the url for the Bayview Hotel entry above)
string(31) "bushmills-inn-hotel-bushmills-2"
Bushmills Inn Hotel
Read 7 reviews on yelp
I suspect this would be a new question rather than clutter/confuse this one further?
END OF EDIT
Note: I'm aware of the need to upgrade to mysqli but I have thousands of lines of legacy code to update. For now I'm working on functionality before reviewing the code for best practice.
Since the yelp.php is sort of a blackbox; the best explanation for this behavior would be that it only set's those variables if it finds a match. Updating your code to this should fix that:
unset($yrating, $numreviews, $ratingimg, $url);
require('yelp.php');
I also noticed this peculiar if-statement, do you realize that's an assignment or is this a copy/paste error? If you want to test (that's what if is for)
if ($yelpID == $searchresults['yelpID']) {

Different PHP script behavior online

I have the following problem. I have a script that gets the Airdate of TV Shows and i alter it before I save it to my database. Locally on my localhost it works perfect, but when I tried it online and uploaded it to my web server it shows a different behavior. I have no idea why it is so.
Here are some examples:
The Data I get: Aired 1/22/12
What the outcome of my script should be: 2012-01-22
What I get online: 2022/12--
The Data I get: Aired 8/29/11
What the outcome of my script should be: 2011-08-29
What I get online: 2029/11--
The Data I get: Airs 2/12/12
What the outcome of my script should be: 2012-02-12
What I get online: 2012/12--
Here is my PHP script:
if(strstr($serie['airdate'], 'Airs')) {
$date = substr($serie['airdate'], 5);
}
if(strstr($serie['airdate'], 'Aired')) {
$date = substr($serie['airdate'], 6);
}
$mm = strstr($date, "/", true);
$mmStrLen = strlen($mm);
if((strlen($mm)) == "1") {
$mm = "0".$mm;
}
$dd = substr($date, $mmStrLen+1);
$dd = strstr($dd, "/", true);
$ddStrLen = strlen($dd);
if((strlen($dd)) == "1") {
$dd = "0".$dd;
}
$yy = substr($date, $mmStrLen+1+$ddStrLen+1);
if((strlen($yy)) == "1") {
$yy = "0".$yy;
}
$serie['date'] = "20".$yy."-".$mm."-".$dd;
$serie['airdate'] is the data I get and $serie['date'] is where the altered value should be saved.
The PHP Version I use locally is 5.3.8 and the one of my webhoster is 5.2.17. But I guess this is not the root of the problem.
Can't format a comment as code, so I must write it as an answer.
Please try this on both sides
$x=explode(' ',$serie['airdate']);
if (sizeof($x)!=2) die("Error in step 1");
$x=explode('/',$x[1]);
if (sizeof($x)!=3) die("Error in step 2");
$serie['date']=sprintf("20%02d-%02d-%02d",$x[2],$x[0],$x[1]);
Take a look at the raw date string you are splitting locally vs at the server. I'm willing to bet that the OS date format is different locally than on the server, and your string splitting code is not designed to handle the different format the server is using.
Thats an awful lot of code to handle dates. Have tried using the strtotime() function? You can pair it with the date() function to easily reformat dates in PHP.

Categories