PHP > Invalid Argument supplied for foreach() - php

In short, I am trying to figure out what is wrong with my foreach statement. I have been trying to work on finding the error for over a day know and I'm running out of time. This program is supposed to parse a json array and post it up to a mysqli database.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
$a = print_r(var_dump($GLOBALS),1);
echo htmlspecialchars($a);
$servername = "#";
$username = "#";
$password = "#";
$dbname = "#";
// Create connection
$conn = mysqli_connect($servername, $username, $password, $dbname);
echo "Connection Successful : ";
// Check connection
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
// Read JSON file
$jsondata = file_get_contents('scripts/AUDIT_DIR/report.json');
echo "JSON File Read : ";
// Convert and Loop
$item = json_decode($jsondata, true);
echo "JSON File Decoded : ";
foreach($item as $arr)
{
$id = $arr["id"];
$hostname = $arr["hostname"];
$ip = $arr["ip"];
$package = $arr["package"];
$publisher = $arr["publisher"];
$origin = $arr["origin"];
$version = $arr["version"];
$size = $arr["size"];
$sql = "INSERT INTO testtable(id, hostname, ip, package, publisher, origin, version, size)
VALUES ('10', '$hostname', '$ip', '$package', '$publisher', '$origin', '$version', '$size')";
if (mysqli_query($conn, $sql))
{
echo "New record created successfully : ";
}
else
{
echo "Error: " . $sql . "<br>" . mysqli_error($conn);
}
}
?>

You likely have an invalid return from your json_decode() you can check this with a var_dump($item); after your json_decode()
In php json_decode() will return NULL if the json cannot be decoded or if the encoded data is deeper than the recursion limit. http://php.net/manual/en/function.json-decode.php
You need to properly guard for such a case that $item === null and not assume you will always get a valid return for your foreach() params.
Example showing your error happens when $item = null
https://3v4l.org/oNr8P

Related

MySQL insert inside foreach only being called once

I have an array $alert_note. I iterate through a loop and fill it up with a few strings:
$n = 0;
$alert_note = array();
$results = array();
while($row = mysqli_fetch_assoc($query)){
//some code in here populates the $results[$n] array with results from $row
$thisnote = "<b>Location alert</b><br>
Alert ID: {$results[$n]['alert-id']}<br>
Start: {$results[$n]['start-formatted']}<br>
End: {$results[$n]['end-formatted']}<br>
Radius: {$results[$n]['radius-km']} km<br>
Distance: {$results[$n]['distance-km']} km<br>
<ul>\n";
//$results[$n]['data'] is a nested array, so iterate through it:
foreach($results[$n]['data'] as $name => $data){
$thisnote .= "<li>$name: $data</li>\n";
}
$thisnote .= "</ul>";
$alert_note[$n] = $thisnote;
$n++;
}
Then I call a foreach function:
foreach($alert_note as $alert_note_contents){
error_log("Note: $alert_note_contents");
mysqli_query($dblink, "INSERT INTO `incident_events` (`incident`, `data`, `time`, `operator`) VALUES ('$incident', '$alert_note_contents', '$now', '$operator')");
}
Each string in $alert_note shows up as expected in the PHP error log, but only the last one is inserted into the MySQL table. No PHP errors are being thrown. Any ideas why this may be?
plz before foreach($alert_note as $alert_note_contents){
do this so i can get the error
var_dump($alert_note );
and do this plz so i can see if there is any error ind db
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error)
{ die("Connection failed: " . $conn->connect_error); }
$sql = "INSERT INTO incident_events (incident, data, time, operator) VALUES ('$incident', '$alert_note_contents', '$now', '$operator')";
if ($conn->query($sql) === TRUE)
{ echo "New record created successfully"; }
else { echo "Error: " . $sql . "<br>" . $conn->error; }
$conn->close();

Why isn't this sending to my mysql server?

So, everytime I go to http://localhost/api/calls.php?gamename=test&gameowner=hi&gameownerid=1&placeid=2&serverjobid=hi&serverid=jaja&serverplayers=1&sendername=bob&senderid=3&senderage=14&senderwarnings=0&calltype=non&reportinfo=hi&suspect=none
it shows absolutely nothing and doesn't send the data to my mysql database.
Here is my code. I removed my mysql info just to be safe.
<?php
$servername = "";
$username = "";
$password = "";
$database = "";
// Establish MySQL Connection
$conn = new mysqli($servername, $username, $password, $database);
// Check connection
if ($conn->connect_error) {
die("MySafeServer Database Connection Failed: " . $conn->connect_error);
}
if (array_key_exists('param',$_GET)) {
$gamename = $_GET['param'];
$gameowner = $_GET['param'];
$gameownerid = $_GET['param'];
$placeid = $_GET['param'];
$serverjobid = $_GET['param'];
$serverid = $_GET['param'];
$serverplayers = $_GET['param'];
$sendername = $_GET['param'];
$senderid = $_GET['param'];
$senderage = $_GET['param'];
$senderwarnings = $_GET['param'];
$calltype = $_GET['param'];
$reportinfo = $_GET['param'];
$suspect = $_GET['suspect'];
mysql_query("INSERT INTO mss_calls3 (gamename, gameowner, gameownerid, placeid, serverjobid, serverid, serverplayers, sendername, senderid, senderage, senderwarnings, calltype, reportinfo, suspect) VALUES ($gamename, $gameowner, $gameownerid, $placeid, $serverjobid, $serverid, $serverplayers, $sendername, $senderid, $senderage, $senderwarnings, $calltype, $reportinfo, $suspect)");
};
?>
#Mark is right, you should stick to using the mysqli functions only.
As #andrewsi says, since you're not querying data, there's nothing in your code that prints whether the insert statement is a success, but only on failure, so I added a "success!" echo. You will still want to query the database to see if the values were inserted.
#Matt and #Mark's points about preparing statements are crucial to sanitizing your input - this is security 101, and you should do some googling on it.
But ultimately, I think #CodeGodie hit on your biggest problem to just getting it working. You assign all your variables to the same value with $_GET['param'] except for "suspect" at the very end. And from the link you posted in the question, there is no "param" in your query string. I'm not entirely sure what you were going for, but I'm assuming you wanted to match the parameter name with the variable name. I don't think it works that way, but the following untested code should get you going:
<?php
$params = array(
"gamename",
"gameowner",
"gameownerid",
"placeid",
"serverjobid",
"serverid",
"serverplayers",
"sendername",
"senderid",
"senderage",
"senderwarnings",
"calltype",
"reportinfo",
"suspect"
);
$cols = "";
$vals = "";
$binding_type = "";
$get_params = array();
// first pass to build the query,
// and validate inputs exist
for ($params as $param) {
if ( isset($_GET["$param"]) ) {
$cols .= "$param,";
$vals .= "?,";
$get_params []= $_GET["$param"];
// determine the binding type as either integer or string
if (is_numeric($_GET["$param"]))
$binding_type .= "i";
else
$binding_type .= "s";
} else die("$param is not set");
}
// trim trailing commas
$cols = rtrim($cols, ",");
$vals = rtrim($vals, ",");
$sql = "INSERT INTO mss_calls3 ($cols) VALUES ($vals);";
$servername = "";
$username = "";
$password = "";
$database = "";
// Establish MySQL Connection
$conn = new mysqli($servername, $username, $password, $database);
// Check connection
if ($conn->connect_error) {
die("MySafeServer Database Connection Failed: " . $conn->connect_error);
}
// prepare statement
$stmt = $conn->prepare($sql) or die($conn->error);
// bind parameters
// watch this is the tricky dynamic part I got help from the following, but may need some work:
// http://stackoverflow.com/questions/627763/php-and-mysqli-bind-parameters-using-loop-and-store-in-array
// http://no2.php.net/manual/en/mysqli-stmt.bind-param.php#89171
call_user_func_array( array($stmt, 'bind_param'), array_merge(array($stmt, $binding_type), $get_params));
// execute
if( $stmt->execute() )
echo "success!";
else
echo $stmt->error;
$stmt->close();
$conn->close();
?>

Parse Large XML File in PHP Efficiently to Generate SQL

I am trying to parsing a large XML file and load it into MySQL. I have used simplexml to parse it, and it works perfectly, but its way to slow for this large XML file. Now i am trying to use XMLReader.
Here is the sample of the XML:
<?xml version="1.0" encoding="UTF-8"?>
<drug type="biotech" created="2005-06-13" updated="2015-02-23">
<drugbank-id primary="true">DB00001</drugbank-id>
<drugbank-id>BIOD00024</drugbank-id>
<drugbank-id>BTD00024</drugbank-id>
<name>Lepirudin</name>
<description>Lepirudin is identical </description>
<cas-number>120993-53-5</cas-number>
<groups>
<group>approved</group>
</groups>
<pathways>
<pathway>
<smpdb-id>SMP00278</smpdb-id>
<name>Lepirudin Action Pathway</name>
<drugs>
<drug>
<drugbank-id>DB00001</drugbank-id>
<name>Lepirudin</name>
</drug>
<drug>
<drugbank-id>DB01373</drugbank-id>
<name>Calcium</name>
</drug>
</drugs>
...
</drug>
<drug type="biotech" created="2005-06-15" updated="2015-02-25">
...
</drug>
Here is my approach using simplexml:
<?php
$xml = simplexml_load_file('drugbank.xml');
$servername = "localhost"; // Example : localhost
$username = "root";
$password = "pass";
$dbname = "dbname";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$xmlObject_count = $xml->drug->count();
for ($i=0; $i < $xmlObject_count; $i++) {
$name = $xml->drug[$i]->name;
$description = $xml->drug[$i]->description;
$casnumber = $xml->drug[$i]->{'cas-number'};
// ...
$created = $xml->drug[$i]['created'];
$updated = $xml->drug[$i]['updated'];
$type = $xml->drug[$i]['type'];
$sql = "INSERT INTO `drug` (name, description,cas_number,created,updated,type)
VALUES ('$name', '$description','$casnumber','$created','$updated','$type')";
if ($conn->query($sql) === TRUE) {
$last_id = $conn->insert_id;
} else {
echo "outer else Error: " . $sql . "<br>" . $conn->error. "<br>" ;
}
}
$conn->close();
It works okay and it gives me 7,789 rows. But, I want to use XMLReader to parse this. But the problem with XMLReader I am finding it give more than 35,000 rows.
If you look at the XML you can see that inside the <drug /> nodes there are also some other <drugs><drug> child nodes. How can I overcome this?
Here is my procedure with XMLReader:
<?php
$servername = "localhost"; // Example : localhost
$username = "root";
$password = "pass";
$dbname = "dbname";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$reader = new XMLReader();
$reader->open('drugbank.xml');
while ($reader->read())
{
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'drug')
{
$doc = new DOMDocument('1.0', 'UTF-8');
$xml = simplexml_import_dom($doc->importNode($reader->expand(),true));
$name = $xml->name;
$description = $xml->description;
$casnumber = $xml->{'cas-number'};
// ...
$sql = "INSERT INTO `drug` (name, description,cas_number,created,updated,type)
VALUES ('$name', '$description','$casnumber','$created','$updated','$type')";
if ($conn->query($sql) === TRUE) {
$last_id = $conn->insert_id;
} else {
echo "outer else Error: " . $sql . "<br>" . $conn->error. "<br>" ;
}
}
}
$conn->close();
With this example, I am finding it give more than 35,000 rows.
Alright, I have a working example for you with much improvement in execution speed, memory usage, and database load:
<?php
define('INSERT_BATCH_SIZE', 500);
define('DRUG_XML_FILE', 'drugbank.xml');
$servername = "localhost"; // Example : localhost
$username = "root";
$password = "pass";
$dbname = "dbname";
function parseXml($mysql)
{
$drugs = array();
$xmlReader = new XMLReader();
$xmlReader->open(DRUG_XML_FILE);
// Move our pointer to the first <drug /> element.
while ($xmlReader->read() && $xmlReader->name !== 'drug') ;
$drugCount = 0;
$totalDrugs = 0;
// Iterate over the outer <drug /> elements.
while ($xmlReader->name == 'drug')
{
// Convert the node into a SimpleXMLElement for ease of use.
$item = new SimpleXMLElement($xmlReader->readOuterXML());
$name = $item->name;
$description = $item->description;
$casNumber = $item->{'cas-number'};
$created = $item['created'];
$updated = $item['updated'];
$type = $item['type'];
$drugs[] = "('$name', '$description','$casNumber','$created','$updated','$type')";
$drugCount++;
$totalDrugs++;
// Once we've reached the desired batch size, insert the batch and reset the counter.
if ($drugCount >= INSERT_BATCH_SIZE)
{
batchInsertDrugs($mysql, $drugs);
$drugCount = 0;
}
// Go to next <drug />.
$xmlReader->next('drug');
}
$xmlReader->close();
// Insert the leftovers from the last batch.
batchInsertDrugs($mysql, $drugs);
echo "Inserted $totalDrugs total drugs.";
}
function batchInsertDrugs($mysql, &$drugs)
{
// Generate a batched INSERT statement.
$statement = "INSERT INTO `drug` (name, description, cas_number, created, updated, type) VALUES";
$statement = $statement . ' ' . implode(",\n", $drugs);
echo $statement, "\n";
// Run the batch INSERT.
if ($mysql->query($statement))
{
echo "Inserted " . count($drugs) . " drugs.";
}
else
{
echo "INSERT Error: " . $statement . "<br>" . $mysql->error. "<br>" ;
}
// Clear the buffer.
$drugs = array();
}
// Create MySQL connection.
$mysql = new mysqli($servername, $username, $password, $dbname);
if ($mysql->connect_error)
{
die("Connection failed: " . $mysql->connect_error);
}
parseXml($mysql);
I tested this example using the same dataset.
Using SimpleXML in the way that you are leads to parsing the entire document in memory, which is slow and memory-intensive. This approach uses XMLReader, which is a fast pull-parser. You can probably make this faster still using the PHP SAX XML Parser, but it's a bit more complex of a pattern, and the above example will be noticeably better than what you started with.
The other significant change in my example is that we're using MySQL Batched Inserts, so we only actually hit the database every 500 (configurable) items we process. You can tweak this number for better performance. After a certain point, the query will become too large for MySQL to process, but you may be able to do a lot more than 500 at one time.
If you'd like me to explain any part of this further, or if you have any problems with it, just let me know in the comments! :)

JSON received as quote (with "" mark)

I keep receiving my JSON from my web service inside quotation marks, like this:
"[{"id":"1","nombre":"Cecilia","correo":"csoto#ts.edu.mx","telefono":"5529964"},{"id":"2","nombre":"Romero","correo":"","telefono":"5435432"},{"id":"3","nombre":"nick","correo":"carlos#mail.com","telefono":"12345"}]"
Notice the "" before the brackets and I don't know why.
I'm using jacwright's restServer (https://github.com/jacwright/RestServer) with this function:
public function getUsers()
{
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "estudiantes";
// Create connection
$array = array();
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT id, nombre, correo, telefono FROM estudiante";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
$res = array("id" => $row["id"], "nombre" => $row["nombre"], "telefono" => $row["telefono"], "correo" => $row["correo"]);
array_push($array, $res);
//echo "id: " . $row["id"]. " - Name: " . $row["nombre"]. " " . $row["correo"]. "<br>";
}
}
$conn->close();
return json_encode($array) ;
}
Can somebody insight me why it keeps adding these quotation marks?
Just a quick example simulating your case, using echo to return JSON encoded array:
JS:
$.get('json.php', function(data) {
console.log(data);
if (data) {
//Check the console, everything is working like charm
newData = $.parseJSON(data);
console.log(data);
console.log('--------------' + 'Elements count:' + newData.length);
console.log('--------------' + newData);
}
});
json.php:
<?php
$myArr = array(
array("id"=>"1","nombre"=>"Cecilia","correo"=>"csoto#ts.edu.mx","telefono"=>"5529964"),
array("id"=>"2","nombre"=>"Romero","correo"=>"","telefono"=>"5435432"),
array("id"=>"3","nombre"=>"nick","correo"=>"carlos#mail.com","telefono"=>"12345")
);
$jsonData = json_encode($myArr);
echo $jsonData;
?>
Same above code using return $jsonData; instead of echo $jsonData; in json.php it gives me the following in the console:
emptiness for data
console error:
ReferenceError: newData is not defined
The reason of getting the quotes in the returned string is because the server (https://github.com/jacwright/RestServer) is doing json_encode itself. So when you return the data from your function getUsers and you have used json_encode it is converted to a json string and when RestServer returns it to you it sees it only as a string therefore adding quotes around it.
If you look at the TestController you will see that it returns an object $user not json_encode($user) and RestServer in it's sendData function actually uses the json_encode function.
So your code needs to be:
public function getUsers()
{
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "estudiantes";
// Create connection
$array = array();
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT id, nombre, correo, telefono FROM estudiante";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
$res = array("id" => $row["id"], "nombre" => $row["nombre"], "telefono" => $row["telefono"], "correo" => $row["correo"]);
array_push($array, $res);
//echo "id: " . $row["id"]. " - Name: " . $row["nombre"]. " " . $row["correo"]. "<br>";
}
}
$conn->close();
return $array;
}
Also check out the function sendData in https://github.com/jacwright/RestServer/blob/master/source/Jacwright/RestServer/RestServer.php#L414 on line 414.

PHP split a string and insert in MySQL

I am using AJAX to send an array of values to a PHP page that will insert the data into MySQL database. The problem is that I am not sure how to split the data into 5 different veriables and loop to insert into DB.
AJAX Request:
Array:
[".testimonial", 1119, 316, 1663, 608, "#header", 723, 66, 1663, 608]
Posting the array:
Sending Array Parameters using " ; " to split.
clicks
.testimonial;1119;316;1663;608;#header;723;66;1663;608
Source Sent:
clicks=.testimonial%3B1119%3B316%3B1663%3B608%3B%23header%3B723%3B66%3B1663%3B608
PHP Page
<?php
$clicks = $_GET["clicks"];
$clickArray = explode(";",$clicks);
$arrays = array_chunk($clickArray, 5);
foreach ($arrays as $array_num => $array) {
foreach ($array as $item_num => $item) {
echo "". $item . "<br/>";
}
}
$servername = "localhost";
$username = "root";
$password = "password";
$dbname = "clickmap";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "INSERT INTO data (user_id, identifier_name, pos_x, pos_y, window_width, window_height, status)
VALUES ('1', '$postIdentifier', '$postPos_x', '$postPos_y','$postWindowWidth','$postWindowHeight', 'ok')";
if ($conn->query($sql) === TRUE) {
echo "New record created successfully";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
?>
Current PHP Result:
.testimonial
1119
316
1663
608
#header
723
66
1663
608
New record created successfully
This should work, but using extract is usually not recommended as it can easily lead to bugs that are hard to debug.
$keys = array('postIdentifier', 'postPos_x', 'postPos_y','postWindowWidth','postWindowHeight');
foreach ($arrays as $array_num => $array) {
$values = array();
foreach ($array as $item_num => $item) {
$values[] = $item;
}
$data = array_combine($keys, $values);
extract($data); // now your variables are declared with the right values http://php.net/manual/en/function.extract.php
// .. run SQL insert here
}

Categories