Magento memory leak in for loop - php

I can't figure out where all my memory is going, memory gets exhausted after about 28,000 loops. have tried several combinations of unset to no avail.
updateProduct() is only commented out to help me isolate the issue.
Any help would be appreciated.
include_once '../app/Mage.php';
Mage::init();
//fetch all products
$res = Mage::getSingleton('core/resource');
$db = $res->getConnection('catalog_read');
$productTable = $res->getTableName('catalog/product');
$sql = $db->select()->from($productTable,
'entity_id'
);
//Returns about 162,000 rows
$rows = $db->fetchAll($sql);
$rowcount = count($rows);
$progress = 0;
foreach($rows as $productRow)
{
$product_id = $productRow["entity_id"];
if($product_id){
//memory leaks here.
$product=Mage::getModel('catalog/product')->setStoreID(0)->load($product_id);
//updateProduct($product);
}else{
echo "LOAD ERROR\n";
}
$product->clearInstance();
unset($product);
gc_collect_cycles();
//echo memory_get_usage() . " 49\n";
if($progress % 500 == 0)
{
echo "$progress out of $rowcount complete\n";
echo memory_get_usage() . "\n";
}
$progress++;
}
updateProduct is matching discount to some layered navigation attributes:
function updateProduct($product_id)
{
global $off10,$off20,$off30,$off40,$off50;
//echo "$product_id\n";
$product=Mage::getModel('catalog/product')->setStoreID(0)->load($product_id);
$attribute = $product->getResource()->getAttribute('discount');
if ($attribute)
{
$discount_value += $attribute->getFrontend()->getValue($product);
//Match discount to layered_nav_discount and all attributes < discount
if($discount_value < 10){
$value = "";
}elseif($discount_value < 20){
$value = "$off10";
}elseif($discount_value < 30){
$value = "$off10,$off20";
}elseif($discount_value < 40){
$value = "$off10,$off20,$off30";
}elseif($discount_value < 50){
$value = "$off10,$off20,$off30,$off40";
}elseif($discount_value >= 50){
$value = "$off10,$off20,$off30,$off40,$off50";
}
//Update layered_nav_discount
//$_product->addAttributeUpdate('layered_nav_discount', $value, 0);
unset($product);
return;
}
};

You can add in top of file for increase memory and time limit.
set_time_limit(0);
ini_set('memory_limit','512M');

Related

How to access and return specific value of a foreach in PHP

So I have a request-response that looks like this
$json='[ {"item_cat":"Stationary","items":[{"item_name":"A4 Paper","qty":"2"},{"item_name":"Test Paper","qty":"6"}],"total":"2"},
{"item_cat":"Computer Accessory ","items":[{"item_name":"Power pack","qty":"2"}],"total":"1"},
{"item_cat":"Material","items":[{"item_name":"T-Shirt","qty":"3"},
{"item_name":"Cap","qty":"5"}],"total":"2"}]';
I'm trying to get each item_name and qty so I can use them to manipulate my db. Here is what I've done
$data = json_decode($json, true);
$len = count($data);
for($i =0; $i< $len; $i++){
$item_length = count($data[$i]['items']);
for($c=0; $c < $item_length; $c++){
foreach ($data[$i]['items'][$c] as $key => $value ) {
$qty = '';
if($key == "qty"){
$qty = $data[$i]['items'][$c];
}
if($key == 'item_name'){
$item_name = "$value";
}
$sql= $db->query("SELECT `stock` from `inventory` WHERE `item_name` = '$item_name'");
while ($sql1 = $sql->fetch_assoc()) {
$stock = $sql1['stock'];
}
if($stock > $qty ){
$stock_balance = $stock - $qty;
$quantity = (int)$qty;
$db->query("UPDATE `inventory` SET `stock` = (`stock` - '$quantity') WHERE `item_name` = '$item_name'");
}else{
echo "<h3> This Operation Not Allowed: Stock Balance Is Less Than The Request <h3>";
}
}
}
}
A non-numeric value encountered, which is as a result of $qty because I'm not able to return just qty value. I've tried several other means. I'm really exhausted. Would appreciate any help please. Cheers!
Let's decompose your code.
This is json:
[
{
"item_cat":"Stationary",
"items":[
{
"item_name":"A4 Paper",
"qty":"2"
},
{
"item_name":"Test Paper",
"qty":"6"
}
],
"total":"2"
},
{
"item_cat":"Computer Accessory ",
"items":[
{
"item_name":"Power pack",
"qty":"2"
}
],
"total":"1"
},
{
"item_cat":"Material",
"items":[
{
"item_name":"T-Shirt",
"qty":"3"
},
{
"item_name":"Cap",
"qty":"5"
}
],
"total":"2"
}
]
Now the array loop without the SQL (to ensure that it works as expected):
$data = json_decode($json, true);
$len = count($data);
for($i =0; $i< $len; $i++){
$item_length = count($data[$i]['items']);
for($c=0; $c < $item_length; $c++){
foreach ($data[$i]['items'][$c] as $key => $value ) {
$qty = '';
if($key == "qty"){
$qty = $data[$i]['items'][$c];
}
if($key == 'item_name'){
$item_name = "$value";
}
The problems here are: un-human variable names and not correct working with JSON object.
First of all, let us rename variables to something readable.
Example:
$data[$i] will be $catalog_entry (object)
$data[$i]['items'] will be $catalog_entry_items (array)
$data[$i]['items'][$c] will be $catalog_entry_item (one item, object)
Let's change the code with new variables:
$data = json_decode($json, true);
$len = count($data);
for($i =0; $i< $len; $i++) {
$catalog_entry = $data[$i];
$catalog_entry_items = $data[$i]['items'];
for($c=0; $c < sizeof($catalog_entry_items); $c++) {
$catalog_entry_item = $data[$i]['items'][$c];
$qty = $catalog_entry_item['qty'];
$item_name = $catalog_entry_item['item_name'];
echo $item_name . ' : ' . $qty . "\n"; // <-- this is for testing
}
}
Run this code and see the expected result:
A4 Paper : 2
Test Paper : 6
Power pack : 2
T-Shirt : 3
Cap : 5
Good, now we have qty and item_name.
Let's make queries. First look at your code:
$sql= $db->query("SELECT `stock` from `inventory` WHERE `item_name` = '$item_name'");
while ($sql1 = $sql->fetch_assoc()) {
$stock = $sql1['stock'];
}
Two strange things: 1) possible SQL injection, 2) replace $stock variable many times with new value (if we have more than 1 row for item in inventory).
Anyway, if this code is working (I can't check), then we come to next part:
if ($stock > $qty ) {
$stock_balance = $stock - $qty;
$quantity = (int)$qty;
$db->query("UPDATE `inventory` SET `stock` = (`stock` - '$quantity') WHERE `item_name` = '$item_name'");
} else {
echo "<h3> This Operation Not Allowed: Stock Balance Is Less Than The Request <h3>";
}
First, unnecessary cast to integer, so remove line $quantity = (int)$qty; and put $stock_balance into query. We will have:
if ($stock >= $qty ) {
$stock_balance = $stock - $qty;
$db->query("UPDATE `inventory` SET `stock` = $stock_balance WHERE `item_name` = '$item_name'");
} else {
echo "<h3> This Operation Not Allowed: Stock Balance Is Less Than The Request <h3>";
}
...well... not only you are exhausted, so I will end now. Ask if something is not correct or not understandable.

How to do operations on nested nested arrays

I need to loop through all pending orders "DONE"
then loop through each order items and save them to an array DONE
then call the api which will return a response like this DONE
{"response":
[{"Lookup":"SKU","Quantity":3,"StoreName":"Shop1"},
{"Lookup":"SKU","Quantity":30,"StoreName":"Shop2"},
{"Lookup":"SKU","Quantity":15,"StoreName":"Shop3"},
{"Lookup":"SKU","Quantity":50,"StoreName":"Shop4"}]}
after that i need to check the quantity from Shop3 first, if it is bigger than the ordered amount, then take it from there and save the new quantity, if there is still remaining quantity, then loop through the rest of the shops, and take from the shop that have the most quantity, and this where I'm Stuck. if I looped through all stores and there is still quantity to get ordered, i should report it too.
What I have done so far is this.
$requiredItems--> is an array of (sku => Ordered Quantity) which is accurate
$report = array();
$allItemsStoreQty = array();
$unavailableItems = array();
foreach ($requiredItems as $sku => $qty) {
$remainingQty = $qty;
$itemStoreQty = array();
//Get Stock Breakdown
$stock = $this->getStock($sku);
if ($stock['Shop3'] >= $qty) {
$storeQty = array();
$storeQty['Shop3'] = $qty;
array_push($itemStoreQty, $storeQty);
} else {
if ($stock['Shop3'] < $qty) {
$storeQty = array();
$storeQty['Shop3'] = $qty;
$remainingQty = $qty - $stock['Shop3'];
$stock['Shop3'] = 0;
array_push($itemStoreQty, $storeQty);
}
arsort($stock);
foreach ($stock as $storeStockName => $storeStockQty) {
if ($remainingQty > 0 && $storeStockQty > 0) {
$currentQty = $remainingQty - $storeStockQty;
if ($currentQty <= 0) {
$storeQty = array();
$storeQty[$storeStockName] = $remainingQty;
array_push($itemStoreQty, $storeQty);
$remainingQty = 0;
} else {
$storeQty = array();
$storeQty[$storeStockName] = $currentQty;
array_push($itemStoreQty, $storeQty);
$remainingQty = $currentQty;
}
}
}
$allItemsStoreQty[$sku] = $itemStoreQty;
}
if ($remainingQty > 0) {
$unavailableItems[$sku] = $remainingQty;
}
}
$report['available'] = $allItemsStoreQty;
$report['unavailable'] = $unavailableItems;
return $report;
but the report that is coming in wrong numbers, I don't know where is my mistake, I know it's somewhere in the logic :( But I can't find it.
as far as i can see, the problem is:
if ($stock['Shop3'] >= $qty) {
$storeQty = array();
$storeQty['Shop3'] = $stock['shop3']; // not $qty
array_push($itemStoreQty, $storeQty);
}
and
} else {
$storeQty = array();
$storeQty[$storeStockName] = $storeStockQty; // not $currentQty;
array_push($itemStoreQty, $storeQty);
$remainingQty = $currentQty;
}

PHP sum each loop value

I wanted to make a simple calculations to summarized what I purchased.
Using $_GET every time the value is updated it should save in an array then when 'start' is executed, it gives the sum. Sorry the codes here are just googled and I'm not really a programmer. I don't know how to combine the two sets of code (array + sum).
$number = $_GET ['input'];
$arr = array ($number);
$data = array($arr);
foreach ($tareas as $tarea) {
$data[] = $tarea;
}
var_dump($data);
$sum = 0;
foreach($group as $key=>$arr) {
$sum+= $arr;
}
echo $sum;
So I got this code from withinweb.com and modified to make it simpler.
<?php session_start();
$products = array($_GET["prod"]);
$amounts = array($_GET ["cost"]);
if ( !isset($_SESSION["total"]) ) {
$_SESSION["total"] = 0;
for ($i=0; $i< count($products); $i++) {
// $_SESSION["qty"][$i] = 0;
$_SESSION["amounts"][$i] = 0;
}
}
//---------------------------
//Reset
if ( isset($_GET['reset']) )
{
if ($_GET["reset"] == 'true')
{
unset($_SESSION["qty"]); //The quantity for each product
unset($_SESSION["amounts"]); //The amount from each product
unset($_SESSION["total"]); //The total cost
unset($_SESSION["cart"]); //Which item has been chosen
}
}
//---------------------------
//Add
if ( isset($_GET["add"]) )
{
$i = $_GET["add"];
$qty = $_SESSION["qty"][$i] + 1;
$_SESSION["amounts"][$i] = $amounts[$i] * $qty;
$_SESSION["cart"][$i] = $i;
$_SESSION["qty"][$i] = $qty;
}
//---------------------------
//Delete
if ( isset($_GET["delete"]) )
{
$i = $_GET["delete"];
$qty = $_SESSION["qty"][$i];
$qty--;
$_SESSION["qty"][$i] = $qty;
//remove item if quantity is zero
if ($qty == 0) {
$_SESSION["amounts"][$i] = 0;
unset($_SESSION["cart"][$i]);
}
else
{
$_SESSION["amounts"][$i] = $amounts[$i] * $qty;
}
}
//cart
if ( isset($_SESSION["cart"]) ) {
$total = 0;
foreach ( $_SESSION["cart"] as $i ) {
echo '' . $products[$_SESSION["cart"][$i]] . ' - ' . $_SESSION["amounts"][$i] . '<br>';
$total = $total + $_SESSION["amounts"][$i];
}
$_SESSION["total"] = $total;
echo'
<br>
Total : ' . $total . '
';
}
?>
When I input ?add=0&prod=apple&cost=100, it gives me:
apple - 100
Total : 100
But when I add another session, ?add=1&prod=orange&cost=200 it doesn't give the right answer.
orange - 100
- 0
Total : 100
It should return me this value, I'm puzzled where could be the error.
apple - 100
orange - 200
Total : 300
Yes, I'm not a coder, but trying to solve a big problem.. :) Thanks for those who help.

Use PHP while loop to create advance filter

I have a problem and that is I want to create a link on a website like people can click the link to show certain products only depending on percentage. like for example, i have a column in my database with discount percentage and it will show min discount and max discount. assuming we have min and max discount. $min=12 and $max=94; and I want to put them in links to show only products with certain discounts only like filtering. below is the example of the link.
<a href="#">12% to 20%</a
21% to 30%
31% to 40% and so on until it reaches
81% to 90% and the last will be
91% to 94%
smallest and largest numbers will be coming from a column from database and they can change frequently. i came up with solution and its working fine but my code is too long and its like I took to many steps which could be done in few lines of code. I have pasted my working code below but I am sure this can be reduced to few lines of code.
$catsql25 = "SELECT MAX(down_percentage) as largest FROM hot_deals";
$catquery25 = mysqli_query($conn, $catsql25);
while ($row25 = mysqli_fetch_array($catquery25, MYSQLI_ASSOC)){
$largest_number = $row25['largest'];
}
$catsql26 = "SELECT MIN(down_percentage) as smallest FROM hot_deals";
$catquery26 = mysqli_query($conn, $catsql26);
while ($row26 = mysqli_fetch_array($catquery26, MYSQLI_ASSOC)){
$smallest_number = $row26['smallest'];
}
$array_tens = array(10,20,30,40,50,60,70,80,90,100);
foreach ($array_tens as $value){
if(($value - $smallest_number <= 10) && ($value - $smallest_number > 0)){
echo '<a href="/exp.php?fst='.$smallest_number.'&lst='.$value.'"><div class="lfmen2">';
echo $smallest_number." to ".$value."</div></a>";
$next_num = $value + 1;
$next_ten = 9;
$stop_num = floor($largest_number / 10);
$stop_num2 = $stop_num * 10;
//echo $stop_num2.'<br>';
$num_rounds = $stop_num2 - $value;
$num_rounds2 = $num_rounds / 10;
//echo $num_rounds2;
for ($i = 1; $i <= $num_rounds2; $i++){
$end_num = $next_num + $next_ten;
echo '<a href="/exp.php?fst='.$next_num.'&lst='.$end_num.'"><div class="lfmen2">';
echo $next_num;
echo " to ";
echo $end_num;
echo "</div></a>";
$next_num += 10;
$end_num += 10;
}
}
}
foreach ($array_tens as $value2){
if(($largest_number - $value2 < 10) && ($largest_number - $value2 > 0)){
$lsst = $value2 + 1;
if($lsst != $largest_number){
echo '<div class="lfmen2">'.$lsst." to ".$largest_number."</div>";
}
elseif($lsst == $largest_number){
echo '<div class="lfmen2">'.$largest_number.'</div>';
}
}
}
I know its all mess but..
Thanks.
First thing you could do is only one SQL Query :
$catsql = "SELECT MAX(down_percentage) as largest, MIN(down_percentage) as smallest FROM hot_deals";
And then you'll need only one loop :
$catquery = mysqli_query($conn, $catsql);
while ($row = mysqli_fetch_array($catquery, MYSQLI_ASSOC)){
$largest_number = $row['largest'];
$smallest_number = $row['smalest'];
}
After that, you could make only one foreach loop. The two "if" conditions could be in the same loop :
foreach ($array_tens as $value) {
if (($value - $smallest_number <= 10) && ($value - $smallest_number > 0)) {
echo '<a href="/exp.php?fst='.$smallest_number.'&lst='.$value.'"><div class="lfmen2">';
echo $smallest_number." to ".$value."</div></a>";
$next_num = $value + 1;
$next_ten = 9;
$stop_num = floor($largest_number / 10);
$stop_num2 = $stop_num * 10;
//echo $stop_num2.'<br>';
$num_rounds = $stop_num2 - $value;
$num_rounds2 = $num_rounds / 10;
//echo $num_rounds2;
for ($i = 1; $i <= $num_rounds2; $i++) {
$end_num = $next_num + $next_ten;
echo '<a href="/exp.php?fst='.$next_num.'&lst='.$end_num.'"><div class="lfmen2">';
echo $next_num;
echo " to ";
echo $end_num;
echo "</div></a>";
$next_num += 10;
$end_num += 10;
}
}
if (($largest_number - $value < 10) && ($largest_number - $value > 0)) {
$lsst = $value + 1;
if ($lsst != $largest_number) {
echo '<div class="lfmen2">'.$lsst." to ".$largest_number."</div>";
} elseif ($lsst == $largest_number) {
echo '<div class="lfmen2">'.$largest_number.'</div>';
}
}
}
To make it more readable, you could also comment your code to know what do what.
This and a good indentation and you're right.
Hope it helps.

Pseudo-code for shelf-stacking

Suppose I have some serially numbered items that are 1-n units wide, that need to be displayed in rows. Each row is m units wide. I need some pseudo-code that will output the rows, for me, so that the m-width limit is kept. This is not a knapsack problem, as the items must remain in serial number order - empty spaces at the end of rows are fine.
I've been chasing my tail over this, partly because I need it in both PHP and jQuery/javascript, hence the request for pseudo-code....
while (!items.isEmpty()) {
rowRemain = m;
rowContents = [];
while (!items.isEmpty() && rowRemain > items[0].width) {
i = items.shift();
rowRemain -= i.width
rowContents.push(i);
}
rows.push(rowContents);
}
Running time is Θ(number of items)
Modulus is your friend. I would do something like:
$items = array(/* Your list of stuff */);
$count = 0;
$maxUnitsPerRow = 4; // Your "m" above
while ($item = $items[$count]) {
if ($count % $maxUnitsPerRow == 0) {
$row = new row();
}
$row->addItemToRow($item);
$count++;
}
Here is an alternative php code ...
function arrayMaxWidthString($items, $maxWidth) {
$out = array();
if (empty($items)) {
return $out;
}
$row = $maxWidth;
$i = 0;
$item = array_shift($items);
$row -= strlen($item);
$out[0] = $item;
foreach ($items as $item) {
$l = strlen($item);
$tmp = ($l + 1);
if ($row >= $tmp) {
$row -= $tmp;
$out[$i] = (($row !== $maxWidth) ? $out[$i] . ' ' : '') . $item;
} elseif ($row === $maxWidth) {
$out[$i] = $item;
++$i;
} else {
++$i;
$row = $maxWidth - $l;
$out[$i] = $item;
}
}
return $out;
}
For what it's worth, I think I have what I was looking for, for PHP - but not sure if there is a simpler way...
<?php
// working with just a simple array of widths...
$items = array(1,1,1,2,1,1,2,1);
$row_width = 0;
$max_width = 2;
echo "Begin\n"; // begin first row
foreach($items as $item=>$item_width) {
// can we add item_width to row without going over?
$row_width += $item_width;
if($row_width < $max_width) {
echo "$item_width ";
} else if($row_width == $max_width) {
echo "$item_width";
echo "\nEnd\nBegin\n"; // end last row, begin new row
$row_width = 0;
} else if($row_width == 2* $max_width) {
echo "\nEnd\nBegin\n"; // end last row, begin new row
echo "$item_width";
echo "\nEnd\n"; // end new row
$row_width = 0;
if($item < count($items)) echo "Begin\n"; // new row
} else if($row_width > $max_width) {
echo "\nEnd\nBegin\n"; // end last row, begin new row
echo "$item_width";
$row_width = $item_width;
}
}
echo "\nEnd\n"; // end last row
?>

Categories