php, control duplicates names when generate - php

i'm new in php. i just want to generate random names (or what you want...). it does works! but when i put in the middle of the code one "do/while" to control and avoid duplicates names it DOES NOT WORKS.... why? what's wrog with my code? i'm exhauste, destroyed.
<?php
require_once 'config.php';
require_once 'dbconn.php';
function getnome() {
$nome = ['tin', 'pin', 'nid', 'din', 'vin'];
return $nome[mt_rand(0, count($nome) - 1)];
}
for ($f =0; $f<6; $f++) {
$arr = [];
do {
$x = getnome();
}
while (in_array($x, $arr));
$arr[]=$x;
$query = "INSERT INTO ants (ant_id, nome) VALUES (NULL, '".getnome()."')";
$res = $mysqli->query($query);
if (!$res) {
echo('<br>Error' . $mysqli->error);
} else {
echo $mysqli->affected_rows . ' created';
}
}
?>
enter image description here

A simpler way to get unique values out of an array is to randomize the array and then process it in order.
$stmt = $mysqli->prepare("INSERT INTO ants (nome) VALUES (?)");
$stmt->bind_param("s", $a_nome);
$nome = ['tin', 'pin', 'nid', 'din', 'vin'];
shuffle($nome);
foreach ($nome as $a_nome) {
$stmt->execute();
}

There are two main problems:
You're reinitializing $arr with each iteration of the for loop, so it isn't actually keeping track of the random values you've already inserted. You need to initialize it before the for loop.
You're not using the non-duplicate value of $x you just got in the do... while loop in your insert statement. You're calling the getnome function again, which will give you another (possibly duplicate) value. You need to use $x instead.
$arr = []; // initialize $arr here instead
for ($f = 0; $f < 6; $f++) {
do {
$x = getnome();
}
while (in_array($x, $arr));
$arr[] = $x;
// use $x instead of getnome()
$query = "INSERT INTO ants (ant_id, nome) VALUES (NULL, '" . $x . "')";
$res = $mysqli->query($query);
if (!$res) {
echo('<br>Error' . $mysqli->error);
} else {
echo $mysqli->affected_rows . ' created';
}
}
Another problem you'll encounter once you've fixed the other two things is that you're trying to insert six unique values, but your getnome function can produce only five, so it looks like the do...while loop will become infinite on the last attempt. (That may just be something that got lost in translation when you created your example, but it is a possibility with while or do...while loops that you need to be careful about.)

Related

PHP Memory Exhaustion, inherited code causes error with larger files, do I flush the memory, batch the processing, or increase memory allocation?

Uploader worked fine until the file became larger than 100,000 lines. I didn't write the code but I want to fix it. I have worked with other languages but not PHP. I know there are different ways to address the issue, but I am unsure of the best investment of time. Ideally I would like uploader to accept files of any size. Changing the memory allocation seems to be the quickest fix, but I would expect long term issues when the file outgrows the memory. Flushing the memory and batching the uploads seem to be 2 sides of the same coin, however the uploader currently will only process a single file and a single upload to the database, every time the file is uploaded it deletes the previous data and replaces it with data from the file. Specifically I have been adjusting the CSV uploader and not the XLSX uploader.
I have already unsuccessfully tried to allocate addition memory to the program but it crashed the server and I would prefer not to do that again. I have also attempted to batch the csv file but it failed as well.
<?php
class Part {
public $id;
public $oem;
public $part_number;
public $desc;
// Assigning the values
public function __construct($id, $oem, $part_number, $desc) {
$this->id = $id;
$this->oem = $oem;
$this->part_number = $part_number;
$this->desc = $desc;
}
}
//imports single csv file and returns an array of Parts
function importCSVpartfinder($filename, $brand, $root){ //$filename is a dataTable of dimensions: first row contains dimension labels, second row are units, the first column is the part number
$handle = fopen($filename, 'r') or die('unable to open file: $filename');
$contents = fread($handle, filesize($filename));
fclose($handle);
$row = explode("\r" , $contents);
$data = array();
$data2 = array();
for ($i=0; $i < sizeof($row); $i++) {
$columns = explode(",", $row[$i]);
array_push($data, $columns);
}
$all = array(); //array of all Parts
//I should probably sanatize here
for ($i=0; $i < sizeof($data); $i++) {
if (sizeof($data[$i]) != 1){
$id = $data[$i][0];
$oem = $data[$i][1];
$part_number = $data[$i][2];
$desc = $data[$i][3];
$obj = new Part($id, $oem, $part_number, $desc);
array_push($all, $obj);
}
}
return $all;
}
//returns a message with # of succes and list of failures //this is slow with large uploads
function addPartsToDB($data, $connection){ //$data is an array of Parts
//delete
$deleteSQL = "DELETE FROM Part_finder WHERE 1";
$res = $connection->query($deleteSQL);
if (!$res){
echo " Failed to delete Part_finder data, ";
exit;
}
//insert
$e=0;
$s=0;
$failures = "";
$d="";
for ($i=0; $i < sizeof($data); $i++) {
$d .= "(".$data[$i]->id.",'".$data[$i]->oem."','".$data[$i]->part_number."','".$data[$i]->desc."'),";
$s++;
}
$d = substr($d, 0, -1);
$sqlquery = "INSERT INTO Part_finder (id_part, oem, part_number, description) VALUES $d";
$res = $connection->query($sqlquery);
if (!$res){
$sqlError = $connection->error;
return ( $s." items failed to update. Database error. ".$sqlError);
}else{
return ( $s." items updated.");
}
/*
for ($i=0; $i < sizeof($data); $i++) {
$d = "(".$data[$i]->id.",'".$data[$i]->oem."','".$data[$i]->part_number."','".$data[$i]->desc."')";
$sqlquery = "INSERT INTO Part_finder (id_part, oem, part_number, description) VALUES $d";
#$res = $connection->query($sqlquery);
if (!$res){
$failures .= $data[$i]->part_number . "
" ;
$e++;
}else{
$s++;
}
}*/
#return $sqlquery;
}
function importXLSXpartfinder($filename, $root){
require($root.'./plugins/XLSXReader/XLSXReader.php');
$xlsx = new XLSXReader($filename);
/* $sheetNames = $xlsx->getSheetNames();
foreach ($sheetNames as $Name) {
$sheetName = $Name;
}*/
$sheet = $xlsx->getSheet("Sheet1");
$rawData = $sheet->getData();
#$columnTitles = array_shift($rawData);
$all = array(); //array of all Parts
for ($i=0; $i < sizeof($rawData); $i++) {
if (sizeof($rawData[$i]) != 1){
$id = $rawData[$i][0];
$oem = $rawData[$i][1];
$part_number = $rawData[$i][2];
$desc = $rawData[$i][3];
$obj = new Part($id, $oem, $part_number, $desc);
array_push($all, $obj);
}
}
return $all;
}
$filename = $file["partfinder"]["tmp_name"];
if($file["partfinder"]["size"] > 100000000){
echo "File too big".$file["partfinder"]["size"];
exit;
}
//$file comes from edit.php
if($file["partfinder"]["type"] === "text/csv" ) {
$a = importCSVpartfinder($filename, $brand, $root);
}elseif ($file["partfinder"]["type"] === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) {
$a = importXLSXpartfinder($filename, $root);
}else{
var_dump($file["partfinder"]["type"]);
echo ".xlsx or .csv file types only";
exit;
}
$b = addPartsToDB($a,$connection);
echo $b;
?>
The memory exhaustion currently occurs on line 25
$columns = explode(",", $row[$i]);
and the error code is
Fatal error: Allowed memory size of 94371840 bytes exhausted (tried to allocate 20480 bytes) in /www/tools/import-csv-partfinder.php on line 25
Ideally I would still like to upload a single file to update the database and I would need to alter additional programs to be able to upload multiple files or not have the database wipe itself during every upload. Unfortunately I am not able to contact the person who wrote the programs originally, so I am pretty much on my own to figure this out.
I'd suggest using a generator to read your CSV rather than reading the whole thing into an array (actually two arrays with the way it's currently written). This way you only hold one line at a time in memory.
function importCSVpartfinder($filename = '') {
$handle = fopen($filename, 'r');
while (($row = fgetcsv($handle)) !== false) {
yield $row;
}
fclose($handle);
}
Then for your database insert function, use a prepared statement and iterate the generator, executing the statement for each row in the file.
function addPartsToDB($parts, $connection) {
$connection->query('DELETE FROM Part_finder');
$statement = $connection->prepare('INSERT INTO Part_finder
(id_part, oem, part_number, description)
VALUES (?, ?, ?, ?)');
foreach ($parts as $part) {
$statement->execute($part);
}
}
These examples are simplified just to show the concept. You should be able to adapt them to your exact needs, but they are working examples as written.
addPartsToDB(importCSVpartfinder($filename), $connection);

Binding fields as values with mysqli?

I have a PHP application I am developing. It contains a function that edits a user in the database. The function accepts false or a value as its arguments; false means that field should be left as is.
Currently, I am using this code:
function edit_user($id, $name = false, $key = false, $mode = false, $notes = false){
$changes = array();
$changestr = "";
$values = array();
if($name !== false){
$changes[] = "name=?";
$values[] = &$name;
}
// 3 more identical ifs
for($i = 0; $i < count($changes); $i++) $changestr .= 's';
$changestr .= 'i';
$values[] = &$id;
array_unshift($values, $changestr);
$query = $db_connection->prepare("UPDATE users SET " . join(",", $changes) . " WHERE userId=?");
call_user_func_array(array($query, "bind_param"), $values);
$query->execute();
return $query->affected_rows >= 1;
}
(Yes, I know the values are not escaped, I do it elsewhere.)
Something, however, is not working here. What I'd like to do to improve it is just have the query like this:
UPDATE users SET name=?,password=?,mode=?,notes=? WHERE userId=?
And then just bind the field name if I need to leave it as is. What I'm asking, is this possible to do with mysqli? Or does the prepared statement break my idea?
UPDATE: What I mean is can I use mysqli like this:
Take this query:
UPDATE users SET name=?,password=?,mode=?,notes=? WHERE userId=?
Update some values but leave some as is, like this:
UPDATE users SET name='Test',password=password,mode=mode,notes='asdf' WHERE userId=723
#Pietu1998 Yes it's more clear.
The answer it's simple: you cant'd do that. You can however get the same result with some tricks as creating some functions like this
function buildSQLComparison($name,$value=false) {
return $name . '=' . ($value)?$value : $name;
}
function buildSQLComparisonForBinding($name,$value=false) {
return $name . '=' . ($value)?'?' : $name;
}
and call it:
$sqlNamePart = buildSQLComparisonForBinding($$name,$name);
$sqlPasswordPart = buildSQLComparisonForBinding($$password,$password);
$param = array($sqlNamePart,$sqlPasswordPart );
$query = $db_connection->prepare("UPDATE users SET " . join(",",$param);
I hope this can be useful for you.
PS: I suggest you to avoid to use reference to var (es:&$name;)
UPDATE:
read at us.php.net/manual/en/mysqli-stmt.bind-param.php, you can do simply build a query doing that:
$stmt = $mysqli->prepare("UPDATE users SET name=?,password=?,mode=?,notes=? WHERE userId=?");
$stmt->bind_param('ssss',
$name?$name:$$name,
$password?$password:$$password,
$mode?$mode:$$mode,
$notes?$notes:$$notes);
I hope this can be useful

Checking a value in a basic multidimensional array

This will be a quick one for most of you I'm sure, but I'm banging my head against the wall trying to teach myself multidimensional arrays.
I simply wish to check whether or not $_FILES["Photo"]["name"] contains empty strings, and such the code below my if statement is executed.
Currently this works, as does my else statment (not shown), however there has to be a cleaner way of writing this??
Many thanks.
if (empty($_FILES["Photo"]["name"][0]) && empty($_FILES["Photo"]["name"][1]) && empty($_FILES["Photo"]["name"][2])) {
$query = "INSERT INTO lot (lotnumber, lottitle, lotdescription, datecreated, lastmodified) VALUES" .
"('$lotnumber', '$lottitle', '$lotdescription', NULL, NULL)";
if (!$mysqli->query($query)) {
echo '<p class="warning">Error executing INSERT query: (' . $mysqli->errno . ') ' . $mysqli->error . "</p>";
}
else
{
echo '<p class="success">The lot has been added to the directory.' . "</p>" . HTML_LINEBREAK;
}
}
You could use an array_filter(), but I don't really see the problem with what you are doing:
$test_array = array_filter($_FILES['Photo']['name'], function($var) {
return empty($var);
});
if (count($test_array) === 3) {
$query = ... // the rest of your code
}
Of course this assumes that there are only three elements in the array. If you only want to check the first 3 elements, you would want to add an array_slice() like this:
$test_array = array_filter(array_slice($_FILES['Photo']['name'], 0, 3), function($var) {
return empty($var);
});
I would have a script that counts the keys within the "name" level.
$count = sizeof($name_of_array->Photo->name);
Then in a for loop check to see if the keys are empty.
for($i = 0; $i <= $count; $i++)
{
if(empty($name_of_array->Photo->name[$i])
{
... continue code
}
else
{
... continue code
}
}

Return array from Recursive - Php

When browsing the binary tree I want to return an array of recursive functions. In particular when an element in binary tree reaching conditions (if statement) is inserted into the array. After all, the element returns an array of all. My code not work !??
function tree_view($clear_static = false,$conn,$index)
{
static $count = 0;
if ($clear_static) {
$count = 0;
}
$q=mysql_query("SELECT user_id FROM thanhvien WHERE gioithieu='".$index."'",$conn);
while($arr=mysql_fetch_assoc($q))
{
if (truongban($conn,$arr["user_id"],1)==true){
$mang[$count]=$arr["user_id"];
$count++;
}
tree_view(false,$conn,$arr["user_id"]);
}
return $mang;
}
$mang1=tree_view (true,$conn,anloc);
print_r($mang1);
Well, the problem I see is that you are not doing anything with the array returned from the recursive call, here in this line:
tree_view(false,$conn,$arr["user_id"]);
I would recommend including both the array and the count on the method parameters (using a static variable is not recommended). So it would be like this:
function tree_view($conn, $index, $mang, &$count)
{
$q=mysql_query("SELECT user_id FROM thanhvien WHERE gioithieu='".$index."'",$conn);
while($arr=mysql_fetch_assoc($q))
{
if (truongban($conn,$arr["user_id"],1)==true){
$mang[$count]=$arr["user_id"];
$count++;
}
tree_view($conn,$arr["user_id"], $mang, $count);
}
return $mang;
}
And you would invoke your method like this:
$mang1[0] = "";
$count = 0;
$mang1 = tree_view ($conn, anloc, $mang1, $count); print_r($mang1);
First $mang isn't initialized, but likely should be:
// Always initialize variables - good style
$mang = array();
Instead of passing this ugly $count variable, just append newly discovered data:
// Will append the right side value as a new last element to $mang
$mang[] = $arr["user_id"];
Next, you need to pass the variable $anloc:
$mang1=tree_view ( true, $conn, $anloc );
This version might work better:
function tree_view($conn, $index, $mang ) {
$q=mysql_query( 'SELECT user_id '
. ' FROM thanhvien '
. ' WHERE gioithieu = "' . mysql_real_escape_string ( $index ) . '"',
$conn
);
while( $arr = mysql_fetch_assoc( $q ) ) {
// better compare type-safe
if ( true === truongban( $conn, $arr["user_id"], 1 ) ){
// append found element
$mang[ ] = $arr["user_id"];
}
tree_view( $conn, $arr["user_id"], $mang );
}
return $mang;
} // tree_view

what's the code meaning?

$file = fopen("test.txt","r");
while($line = fgets($file)) {
$line = trim($line);
list($model,$price) = preg_split('/\s+/',$line);
if(empty($price)) {
$price = 0;
}
$sql = "UPDATE products
SET products_price=$price
WHERE products_model='$model'";
// run the sql query.
}
fclose($file);
the txt file like this:
model price
LB2117 19.49
LB2381 25.99
1, what's the meaning of list($model,$price) = preg_split('/\s+/',$line);
i know preg_split like explode, but i don't know what't the parameter meaning of the above line
2, how to skip the first record.
it's taking the results of the preg_split and assigning them to the vars $model and $price. You're looking at a parsing algorithm. Sorry if this is not enough. I have a hard time understanding the question as it is written.
Also, if I read this correctly, there is no need to skip line 1 unless you have an item with the model defined as "model" in the database.
But if you wanted to for some reason, you could add a counter...
$i = 0;
while($line = fgets($file)) {
if($i > 0)
{
$line = trim($line);
list($model,$price) = preg_split('/\s+/',$line);
if(empty($price)) {
$price = 0;
}
$sql = "UPDATE products
SET products_price=$price
WHERE products_model='$model'";
// run the sql query.
}
$i++;
}
That is a language construct that allows you to assign to multiple variables at once. You can think of it as array unpacking (preg_split returns an array). So, when you do:
<?php
list($a, $b) = explode(".","a.b");
echo $a . "\n";
echo $b . "\n";
You will get:
a
b
Having less elements in list than the array is ok, excess elements in array are ignored, but having insufficent elements in array will give you an undefined index error. For example:
list($a) = explode(".","a.b"); // ok
list($a,$b,$c) = explode(".","a.b") // error
I don't know if you meant that by skip the first record but...
$file = fopen("test.txt","r"); // open file for reading
$first = true;
while($line = fgets($file)) { // get the content file
if ($first === true) { $first = false;}//skip the first record
else{
$line = trim($line); // remove the whitespace before and after the test
// not in the middle
list($model,$price) = preg_split('/\s+/',$line); // create two variable model and price, the values are from the preg_split \s means whitespace, tab and linebreak
if(empty($price)) { // if $price is empty price =0
$price = 0;
}
$sql = "UPDATE products // sql update
SET products_price=$price
WHERE products_model='$model'";
// run the sql query.
}
}
fclose($file); //close the file

Categories