Pass a Variable in PHP to Query Database for Deletion - php

I am trying to create a cron job in php that deletes disabled users found in a csv file and logs the deleted users to a txt file. Everything works except only the last user in the csv file is deleted. Here is what I have so far:
class purgeInactive extends JApplicationCli
{
public function doExecute()
{
$file = '../tmp/purge_user.csv';
$contents = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$csvRows = array_map('str_getcsv', $contents);
$log = ('../log/purged_users.txt');
$today = date('F j, Y, g:ia');
$row = 1;
if (($handle = fopen("../tmp/purge_user.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
$num = count($data);
$row++;
for ($c=0; $c < $num; $c++) {
file_put_contents($log, PHP_EOL . 'Removed Disabled User(s): ' . $data[$c] . PHP_EOL . '-' . $today . PHP_EOL, FILE_APPEND);
$disUsers = implode(',', $data);
echo ', ' . $disUsers ; // to test $disUsers output
} // end for statement
} // end while statment
} // end if statement
fclose($handle);
$db = JFactory::getDbo();
$user = JFactory::getUser();
$query = $db->getQuery(true);
$userArray = var_export($disUsers,true);
echo PHP_EOL.'This is to test the output of $userArray'.$userArray;
$query
->select($db->quoteName(array('id')))
->from($db->quoteName('#__users'))
//->delete ($db->quoteName('#__users'))
//->where(array($db->quoteName('username') . ' IN(' . $userArray) . ')');
->where(array($db->quoteName('username') . ' = ' . $userArray));
//->where($deleteReq);
$db->setQuery($query);
//$result = $db->execute();
$result = $db->query();
}
}
JApplicationCli::getInstance('purgeInactive')->execute();
Any suggestions on how to run each user in the csv file individually? I am about brain dead on this as I have been working on it too long.
Note: I am running Joomla that uses ldap and I use echo for $userArray to check results.

I think instead of ...
->where(array($db->quoteName('username') . ' = ' . $userArray));
You just want
->where(array($db->quoteName('username') . ' IN(' . $userArray) . ')');
That's assuming you want to delete all the rows whose usernames are in $userArray.
If the values in $userArray aren't necessarily SQL-safe, you might also want to do this first:
$userArray = array_map('mysql_real_escape_string', $userArray);

Each row of your csv is iterating this line: $disUsers = implode(',', $data);
This means that all usernames in that row will be saved to $disUsers, then the next iteration will overwrite the old data with the new row's usernames -- this is why only the final row of data is making it down to your delete query.
$data[$c] is what should be pushed into a $disUsers array like this: $disUsers[] = $data[$c];
In your query, you'll need to quote-wrap each array value; this will do that job:
->where($db->qn('username') . ' IN (' . implode(',', array_map(function($username)use($db) {return $db->q($username);}, $disUsers)) . ')')
Here is a post of mine where I discuss writing Joomla queries with IN: https://joomla.stackexchange.com/a/22898/12352

Related

PHP with JSON issue

I'm new to PHP and I've encountered an issue that is driving me crazy. Perhaps someone here can let me know what I'm doing wrong.
I have a from that a user fills out. The below script is using the date entered into the mysql database to generate json data:
<?php
include("../includes.php");
$sq = new SQL();
$TableName = "permissions";
$Fields = $_POST["Fields"];
$FieldsSTR = "`" . str_replace("*;*","`,`", $Fields) . "`";
$Join = "AND";
$Start = 0;
$Limit = $_POST["Limit"];
if($Limit == "")$Limit = 1000;
$Where = $_POST["Where"];
$WhereSTR = "";
if($Where !== "")$WhereSTR = str_replace("*;*"," $Join ", $Where);
$q = "SELECT $FieldsSTR FROM `$TableName` $WhereSTR";
$data = $sq->sqlQuery($q);
if(sizeof($data)>0)array_shift($data);
header("Content-Type: application/json");
echo "[";
foreach($data as $k=>$line){
echo "[";
echo "\"" . str_replace("*;*","\",\"",$line) . "\"";
echo "]";
if($k < sizeof($data) - 1)echo ",";
}
echo "]";
exit;
?>
The problem that I'm having is that it has stopped working. One day it's fine and the next day it's not working. I'm thinking that maybe the cause of this problem is that crazy user data has been entered into the database. In my foreach statement I tried to replace the ";" with a "" tag, but that didn't work.
Has anyone encountered this issue before? Perhaps someone can point me in the right direction!
Thanks
Jason
Thanks everyone for your input. I was able to stumble on an fix to my immediate problem. I changed my foreach loop to the following:
foreach($data as $k=>$line){
$parts = explode("*;*",$line);
$NEWLINE = array();
for($i = 0;$i < sizeof($parts);$i++){
$value = $parts[$i];
$value = str_replace("\r","<br>",str_replace("\n","<br>",$value));
$NEWLINE[] = "\"" . $value . "\"";
}
$FINDATA[] = "[" . implode(",",$NEWLINE) . "]";
}
But I will now look into into using json_encode() as mentioned in the comments.
Thanks,
Jason

implode() an array with multiple database columns, except on last entry PHP

I am using PHP to create an XML document from a database to import into Adobe InDesign. I want to be able to add a comma after each entry but not on the last one. I have tried using implode() but I have had no luck. Any help would be greatly appreciated. Here is the code with out any attempt at adding the comma. I can just add a comma after the closing but that will still give me one on the last entry. Any advice on how to attack this would be much appreciated. Thanks!
function getAssocXML ($company) {
$retVal = "";
// Connect to the database by creating a new mysqli object
require_once "DBconnect.php";
$staffResult = $mysql->query("
SELECT company,
fName,
lName,
title,
contact1,
contact2
FROM staff
WHERE company = '$company'
AND title
LIKE '%Associate%'
AND archive = 0
");
if ($staffResult->num_rows >= 1 && $staffResult->num_rows < 4) {
$retVal = $retVal;
for ($i = 0; $i < $staffResult->num_rows; $i++) {
// Move to row number $i in the result set.
$staffResult->data_seek($i);
// Get all the columns for the current row as an associative array -- we named it $aRow
$staffRow = $staffResult->fetch_assoc();
// Write a table row to the output containing information from the current database row.
$retVal = $retVal . "<staff>";
$retVal = $retVal . "<name>" . $staffRow['fName'] . " " . $staffRow['lName'] . "</name>";
$retVal = $retVal . "<contact>" . staffContact($staffRow['contact1'], $staffRow['contact2']) . "</contact>";
$retVal = $retVal . "</staff>";
}
$retVal = $retVal . " — Associate
";
$staffResult->free();
}
if ($staffResult->num_rows > 4) {
$retVal = $retVal;
$retVal = $retVal . "<staffHeader>Associates: </staffHeader>";
for ($i = 0; $i < $staffResult->num_rows; $i++) {
// Move to row number $i in the result set.
$staffResult->data_seek($i);
// Get all the columns for the current row as an associative array -- we named it $aRow
$staffRow = $staffResult->fetch_assoc();
// Write a table row to the output containing information from the current database row.
$retVal = $retVal . "<staff>";
$retVal = $retVal . "<name>" . $staffRow['fName'] . " " . $staffRow['lName'] . "</name>";
$retVal = $retVal . "<contact>" . staffContact($staffRow['contact1'], $staffRow['contact2']) . "</contact>";
$retVal = $retVal . "</staff>";
}
$retVal = $retVal . "
";
$staffResult->free();
}
return $retVal;
}
print getAssocXML(addslashes($aRow['company']));
You can do it with the help of this query
echo join(' and ', array_filter(array_merge(array(join(', ', array_slice($array, 0, -1))), array_slice($array, -1)), 'strlen'));
OR
$last = array_slice($array, -1);
$first = implode(', ', array_slice($array, 0, -1));
$both = array_filter(array_merge(array($first), $last), 'strlen');
echo implode(' and ', $both);
I'm afraid that I don't readily grasp your intent here. I don't see anything in your code that is using any sort of array, which is a pre-requisite for implode() (a.k.a. join())
However, here's a little trick that I've used numerous times when I am building something in a loop:
$result = "";
$comma = "";
foreach (...) {
.. calculate some $value ..
$result = $result . $comma . $value;
$comma = ",";
}
The first time through the loop, $comma isn't a comma! It's an empty string. At the end of the first iteration through the loop, it becomes a comma, for use next time. It's a simple but effective way to build an arbitrarily-long "comma-separated string" in-place.
HTH!

sort informations from a file .txt using php

I use php in order to save all informations concerning my user (where do they navigated and at which time).
So I use the following code for saving informations:
<?php
function iif($condition, $true, $false ) {
return ($condition ? $true : $false);
}
$ipaddress = $_SESSION['login'];
$page = "http://{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}";
$page .= iif(!empty($_SERVER['QUERY_STRING']), "?{$_SERVER['QUERY_STRING']}", "");
$referrer = $monUrl = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$datetime = mktime();
$useragent = $_SERVER['HTTP_USER_AGENT'];
$remotehost = #getHostByAddr($ipaddress);
// Create log line
$logline = $ipaddress . '|' . $referrer . '|' . $datetime . '|' . $useragent . '|' . $remotehost . '|' . $page . "\n";
// Write to log file:
$logfile = 'logfile.txt';
$path='log/'.$_SESSION['login'].'/'.$logfile;
if(!file_exists($path)) {
mkdir('log');
mkdir('log/'.$_SESSION['login']);
$f = fopen('log/'.$_SESSION['login'].'/'.$logfile, "x+");
// fermeture
fclose($f);
}
// Open the log file in "Append" mode
if (!$handle = fopen($path, 'a+')) {
die("Failed to open log file");
}
// Write $logline to our logfile.
if (fwrite($handle, $logline) === FALSE) {
die("Failed to write to log file");
}
fclose($handle);
?>
And I use the following code below to display it:
<?php
$path= 'log/'.$data['login'].'/logfile.txt';
$logfile = $path;
if (file_exists($logfile)) {
$handle = fopen($logfile, "r");
$log = fread($handle, filesize($logfile));
fclose($handle);
} else {
die ("Le fichier de Log n'existe pas!");
}
// Seperate each logline
$log = explode("\n", trim($log));
//After that it may be useful to get each part of each logline in a separate variable. This can be done //by looping through each logline, and using explode again:
// Seperate each part in each logline
for ($i = 0; $i < count($log); $i++) {
$log[$i] = trim($log[$i]);
$log[$i] = explode('|', $log[$i]);
}
// Show a table of the logfile
echo '<table id="box-table-a">';
echo '<th scope="col">Agent</th>';
echo '<th scope="col">Referrer</th>';
echo '<th scope="col">Date</th>';
echo '<th scope="col">Useragent</th>';
echo '<th scope="col">Remote Host</th>';
foreach ($log as $logline) {
echo '<tr>';
echo '<td><a href="index.php?p=voir_fiche_employee&id='.$_GET['id'].'"/>' . $logline['0'] . '</a></td>';
echo '<td><a href="'.urldecode($logline['1']).'"/>'. urldecode($logline['1']) . '</a></td>';
echo '<td>' . date('d-m-Y h:i:s', $logline['2']) . '</td>';
echo '<td>' . $logline['3'] . '</td>';
echo '<td>' . $logline['4'] . '</td>';
echo '</tr>';
}
echo '</table>';
?>
The trouble I met is that I really do not know how to sort informations from this file.
In fact on the top of the page I have a form in order to choose between the dates. I wsould like to display all informations from this logfile between those dates but I really do not know how to do.
At the beginning I was planning to save all in the databse, but somebody told me that it would have been too much for the databse, so I decided to save all in a txt on the server.
Does anyone know if it is possible to sort informations from txt file?
By default I would like to display informations of the logfile for the date of the day, and If I want I can choose an other dates or an interval of dates.
If possible to store data into a database. It is easy to sort in database. Reading data into a text file is more time consuming and difficult to sort.

php script running out of memory when large amounts of data being written to file in batches

I am having continuous problems with my script running our of memory.
I need the script to loop through each customer in a database and then to get all the product data and geneate a text file. Each customer can have anything between 1 and 100,000 products.
I bring out the product data in batches of 1,000 and write to a file to try and stop the script from timing out. This has improved a great deal, however, I am still having issues with customers what have large numbers of products. It seems to have issues with customers that have over 5,000 products.
It seems to stop writing to file after the 5th batch (5,000 prods) but the browser just hangs as though it is still generating the file but the product no in the file never increase.
Can anyone help?
set_time_limit(0);
$db = new msSqlConnect('db');
$select = "SELECT customer FROM feeds ";
$run = mssql_query($select);
while($row = mssql_fetch_array($run)){
$arg = $row['customer'];
$txt_file = 'shopkeeper/'. $arg . '.txt';
$generated = generateFeed($db, $arg, $txt_file);
if ($generated){
$update = "UPDATE feeds SET lastGenerated = '$generated' WHERE customer = '$arg' ";
mssql_query($update);
}
}
function generateFeed($db, $customer, $file){
//if file already exists then delete file so can write new file
if (file_exists($file)){
unlink($file);
}
$datafeed_separator = "|";
//get product details
$productsObj = new Products($db, customer)
//find out how many products customer has
$countProds = $productsObj->countProducts();
$productBatchLimit = 1000;
//create new file
$fh = fopen($file, 'a');
$counter = 1;
for ($i = 0; $i < $countProds; $i += $productBatchLimit) {
$txt = '';
$limit = $productBatchLimit*$counter;
$products = $productsObj->getProducts($i, $limit);
foreach($products as $product){
$txt .=
$prod_name . $datafeed_separator .
$prod_brand . $datafeed_separator .
$prod_desc . $datafeed_separator .
$prod_price . $datafeed_separator . "\n";
}
}
fwrite($fh, $txt);
flush();
$counter++;
}
fclose($fh);
$endTime = date('Y-m-d H:i:s');
return $endTime;
}
I can see one thing that might help on your memory usage. If you move the fwrite() inside the foreach loop, you can free up $txt also inside the loop. So it would be something like:
foreach($products as $product){
$txt =
$prod_name . $datafeed_separator .
$prod_brand . $datafeed_separator .
$prod_desc . $datafeed_separator .
$prod_price . $datafeed_separator . "\n";
fwrite($fh, $txt);
}
This will prevent $txt growing large if you have many products.

Magic quote and mysql_real_escape_string

Here is the original code:
if (($handle = fopen($source_file, "r")) !== FALSE) {
$columns = fgetcsv($handle, $max_line_length, ",");
foreach ($columns as &$column) {
$column = str_replace(".","",$column);
}
while (($data = fgetcsv($handle, $max_line_length, ",")) !== FALSE) {
while(count($data) < count($columns)) {
array_push($data, NULL);
}
$c = count($data);
for($i = 0; $i < $c; $i++) {
$data[$i] = "'{$data[$i]}'";
}
$sql[] = '(' . implode(',', $data) . ", '" . $_POST['custgroup'] . "'," . $_POST['user_id'] . ')';
}
$query = "INSERT INTO $target_table (" . implode(',', $columns) .
',custgroup,user_id) VALUES ' . implode(',', $sql);
//mysql_query($query) or trigger_error(mysql_error());
echo $query;
fclose($handle);
}
But once I added mysql_real_escape_string:
$query = "INSERT INTO $target_table (" . implode(',',array_map('mysql_real_escape_string', $columns)) .
',custgroup,user_id) VALUES ' . implode(',',array_map('mysql_real_escape_string', $sql));
The query will become :
INSERT INTO UserAddedRecord (lastname,firstname,ceLL,fax,email,code,custgroup,user_id) VALUES (\'Last\',\'First\',\'01122331\',\'\',\'abc#hotmail.com\',\'12345\', \'\',17)
I checked my php.ini and get_magic_quotes_gpc(),magic quote is disabled.
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off
What should be the problem?or I should just apply stripslashes()?But I assume it will be used only when magic quote is enabled.
You should to something along the lines of:
use mysql_real_escape_string on all string values
cast all the interters to (int)
after that apply the surrounding single quotes ( ' )
The point of mysql_real_escape_string is to escape the special chars within your test content so using stripslashes would take that away.
Another way to solve that is to look into preparedStatements where you don't need to to client side esacping.
Just remember that the whole point of doing this is to prevent SQL-Injection and act accordingly :)

Categories