I am getting "Fatal error: Allowed memory size of XXXX bytes exhausted...". I need to iterate through big amount of records, and execute a function to verify of the record fit the criteria which declare many class variables.
foreach ($results as $row)
{
$location = Location::parseDatabaseRow($row);
if ($location->contains($lat, $lon))
{
$found = true;
$locations[] = $location;
break;
}
}
Implementation of the Location class:
public function contains($lat, $lon)
{
$polygon =& new polygon();
.... //Add points to polygons base on location polygons
$vertex =& new vertex($lat, $lon);
$isContain = $polygon->isInside($vertex);
$polygon->res(); //Reset all variable inside polygons
$polygon = null; //Let Garbage Collector clear it whenever
return ($isContain);
}
Shouldn't the $polygon be clear when contain() method is return? What can I do to reduce memory usage?
I am a Java developer, and started to learn PHP. Please help me understand on how to manage stack size and memory allocation and delocation. Thanks in advance.
I am a Java developer, and started to learn PHP.
Here are some corrections which may allow your code to not exhaust memory limit.
use a while. Since your result comes from a database query, you should have the possibility to use fetch() instead of fetchAll() which I assume you are using since you are applying a foreach() on it.
while ($row = $result->fetch()) { // here $result is supposed to be a PDOStatatement.
$location = Location::parseDatabaseRow($row);
if ($location->contains($lat, $lon)) {
$found = true; // where is this used?
$locations[] = $location;
break;
}
}
While uses less memory because not all results are fetched at the same time.
use the ampersand the right way. You are doing a new in each loop. The ampersand is used when you want to pass a value by reference to a function, so that it is affected outside that function scope without the need to return it.
Here, you are using objects, which are somewhat passed by reference by design.
public function contains($lat, $lon) {
$polygon = new polygon();
$vertex = new vertex($lat, $lon);
return $polygon->isInside($vertex);
// no need to reset the values of your polygon, you will be creating a new one on the next loop.
}
for completeness sake here a version using the same polygon object. Notice how I do not use an ampersand because we are passing an object.
$polygon = new polygon();
while ($row = $result->fetch()) { // here $result is supposed to be a PDOStatatement.
$location = Location::parseDatabaseRow($row);
if ($location->contains($lat, $lon, $polygon)) {
$found = true; // where is this used?
$locations[] = $location;
break;
}
}
public function contains($lat, $lon, $polygon) {
//Add points to the passed polygon
$vertex = new vertex($lat, $lon);
$isContain = $polygon->isInside($vertex);
$polygon->res();
// since we eill be using the same $polygon, now we need to reset it
return $isContain;
}
Related
I have been through S/O and have not found this specific problem although my inexperience may mean I've searched by the wrong terms. Any help would be appreciated. Thank you.
Basically, I have three PHP functions (within a class named KeyPositions). The first is called by passing an argument; the second function is called by the first and then produces an array that is passed on to the third function. By using echo and print_r I can see that everything is in place as it is being passed. The problem is occurring on the return from the final function (the 3rd). While my foreach/echo test (just before the return) shows every element in the array is present, that is not what I'm getting once I look at what is returned to the original call. Here are the steps:
Originating call: $keypositions = KeyPositions::getKPSponsor($session_userid);
Second return (where the problem starts?): return self::getKPList($sponsor_list);
Third function: public static function getKPList($arr)
Test before return (shows array contents perfectly): foreach($kppositions as $s) { echo $s['kp_root'],"<br>"; }
Return statement: return $kppositions;
But when I go back to the original function call and run this test I only receive a subset of what I would've expected from the return (above): foreach($keypositions as $t) { echo $t['id'],"<br>"; }
Is there something that I'm missing when an array is passed from one function to the next? All arrays are associative. I intentionally did not include the lengthy code within each of the functions because the functions themselves seem to be performing perfectly based on the test I do just before each return (meaning what I expect to be inside the array is fully intact at each step). I believe this may have something to do with the passing of returns from one function to the next? Any direction here would be appreciated. Thank you.
Okay. Sorry. My thought was to not muddy things with all the code. Here it is. Keep in mind, this is for testing and not for final (e.g., no PHP injection work yet.)
public static function getKPSponsor($userid)
{
return self::getKPSponsors('positions.jobholder_uid = "' .$userid. '"');
}
public static function getKPSponsors($criteria = '')
{
$sponsor_levels = array();
$sponsor_list = array();
$criteria = " WHERE $criteria";
$db = Database::getInstance();
$mysqli = $db->getConnection();
$sql_query_sponsors = "SELECT positions.position_id AS id, jobholder_uid AS uid FROM positions $criteria";
$result_sponsors = $mysqli->query($sql_query_sponsors);
while ($sponsor_level = mysqli_fetch_assoc($result_sponsors)){
$sponsor_id = $sponsor_level['uid'];
$sponsor_level['items'] = self::getKPSponsors('positions.positionmgr_uid = "' .$sponsor_id. '"');
$sponsor_levels[] = $sponsor_level;
}
foreach($sponsor_levels as $r) {
$sponsor_list[] = $r['id'];
}
mysqli_free_result($result_sponsors);
unset($r);
return self::getKPList($sponsor_list);
}
public static function getKPList($arr)
{
$kppositions = array();
$db = Database::getInstance();
$mysqli = $db->getConnection();
$sql_query_keypositions = "SELECT key_positions.kp_id AS id, key_positions.kp_root, key_positions.kp_area, key_positions.c_ksa, key_positions.c_relationship, key_positions.c_rare, key_positions.urgency, positions.jobholder_uid, master.last_name, CONCAT(master.first_name,' ',master.last_name) AS full_name FROM key_positions INNER JOIN positions ON positions.position_id = key_positions.kp_sponsor INNER JOIN master ON positions.jobholder_uid = master.email_address WHERE key_positions.kp_sponsor IN (" . implode(",", $arr) . ");";
$result = $mysqli->query($sql_query_keypositions);
while ($kp3 = mysqli_fetch_assoc($result)) {
$kppositions[] = $kp3;
}
foreach($kppositions as $s) { // My testing ...
echo $s['kp_root'],"<br>";
}
mysqli_free_result($result);
return $kppositions;
}
In case this helps further, the output from my test BEFORE the return yields:
1000014
1000015
1000016
1000012
1000006
1000022
1000017
1000018
1000019
1000020
1000021
1000008
1000009
1000010
1000011
1000004
1000005
1000007
1000013
1000003
1000001
1000002
The output AFTER the return yields:
1000001
1000002
I'm refactoring all the codes given to me and I saw in the code that the're so many repeated variables that is using with other methods
which is this
$tag_id = json_decode($_POST['tag_id']);
$tag_name = json_decode($_POST['tag_name']);
$pname = json_decode($_POST['pname']);
$icode = json_decode($_POST['icode']);
$bname = json_decode($_POST['bname']);
$client = json_decode($_POST['client']);
$package = json_decode($_POST['package']);
$reference = json_decode($_POST['reference']);
$prodcat = json_decode($_POST['prodcat']);
$physical_array = json_decode($_POST['physical_array']);
$chemical_array = json_decode($_POST['chemical_array']);
$physpec_array = json_decode($_POST['physpec_array']);
$micro_array = json_decode($_POST['micro_array']);
$microspec_array = json_decode($_POST['microspec_array']);
$prod_type = json_decode($_POST['prod_type']);
$create_physical_id_array = json_decode($_POST['create_physical_id_array']);
$create_chemical_id_array = json_decode($_POST['create_chemical_id_array']);
$create_micro_id_array = json_decode($_POST['create_micro_id_array']);
my question is how can i just use put it in one method and i'll just call it to other methods instead of repeating that code.
thank you
You can't call it from other methods. Because the variables defined to that method are local. Instead you can define the variables as member variables of that class and access from any method or even from outside class depending upon the access specifier.
protected $a=2;
public function method1()
{
echo $this->a;
}
public function method2()
{
echo $this->a;
}
Put it in an array
$post_array = [];
foreach($_POST as $key => $value)
{
$post_array[$key] = json_decode($value);
}
return $post_array;
and then call it like this
$post_array['create_micro_id_array'];
Since it appears you are very much aware of the variable names you want to use... I'll suggest PHP Variable variables
To do this, you can loop through your $_POST and process the value while using the key as variable name.
foreach($_POST as $key => $value)
{
$$key = json_decode($value);
}
At the end of the day, these 4 lines would generate all that... However i'm not so sure of how friendly it may appear to someone else looking at the code. Give it a shot.
You may try extract function.
extract($_POST);
Using the Rackspace CloudFiles API (in PHP), there are times when I need to get just a list of the all the current files in the container. What I just came up with is terribly slow and in-efficient because it gets every object pertaining to that file. So what I have:
My Function
function clean_cdn() {
$objects = $this->CI->cfiles->get_objects();
foreach ($objects as $object) {
echo $object->name;
}
}
get_objects wrapper for CodeIgniter
public function get_objects() {
$my_container = $this->container_info();
try {
return $my_container->get_objects(0, NULL, NULL, NULL);
} catch(Exception $e) {
$this->_handle_error($e);
return FALSE;
}
}
cloudfiles get_objects function
function get_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL)
{
list($status, $reason, $obj_array) =
$this->cfs_http->get_objects($this->name, $limit,
$marker, $prefix, $path);
if ($status < 200 || $status > 299) {
throw new InvalidResponseException(
"Invalid response (".$status."): ".$this->cfs_http->get_error());
}
$objects = array();
foreach ($obj_array as $obj) {
$tmp = new CF_Object($this, $obj["name"], False, True);
$tmp->content_type = $obj["content_type"];
$tmp->content_length = (float) $obj["bytes"];
$tmp->set_etag($obj["hash"]);
$tmp->last_modified = $obj["last_modified"];
$objects[] = $tmp;
}
return $objects;
}
This will give me just the name (which is all I need for what I'm doing currently) but is there a better way?
Update
I noticed that I could technically just put all the "directories" in an array and iterate over them in a foreach loop, listing each of them as the 4th parameter of get_objects. So get_objects(0, NULL, NULL, 'css'), etc. Still seems like there's a better way though.
If you are using the old php-cloudfiles bindings, use the list_objects() method. This will just return a list of the objects in the container.
php-cloudfiles bindings are deprecated now, the new official php cloudfiles bindings are php-opencloud (object-store) and you can find the section on listing objects in a container here
Using php-opencloud, if you have a Container object, use the ObjectList() method to return a list of objects:
$list = $container->ObjectList();
while ($obj = $list->Next()) {
// do stuff with $obj
}
The $obj has all of the metadata associated with the object that's returned by the list (which is to say, there are certain attributes that can only be retrieved by invoking the object directly, but this should have most of what you need).
I have a static method 'findAll' on a model which basically gets all rows with certain criteria. This method works fine and I can call it using:
$m::findAll();
Where $m is the model name as a variable. I can output this and it returns correct results. However, when assigning this to a variable in the Zend_View object, as:
$this->view->viewvariable = $m::findAll();
I get the error:
Zend_Db_Table_Exception: Too many
columns for the primary key
Any ideas why?
Find all function:
final public static function findAll($where = false, array $options = array()) {
$object = new static();
if (!empty($options)) $options = array_merge($object->options, $options);
else $options = $object->options;
$run = $object->buildDefaultSelect($where, $options);
$rows = $run->fetchAll();
if ($options['asObject'] == true) {
$result = array();
foreach ($rows as $r) {
$class = new static();
$class->setInfo($r);
$result[] = $class;
}
return $result;
} else {
if (count($rows) > 0) return $rows;
else return array();
}
}
Note: This function works fine everywhere apart from when assigning to a view variable. If I run the below (not assigning it to a view variable), it shows the correct array data.
var_dump($m::findAll($module['where'], $module['options']));
exit;
In my view (I have replaced the actual name with viewvariable for the sake of this post):
<?php foreach($this->viewvariable as $item) { ?>
//Do some echoing of data in $item
//Close foreach
I doubt the issue is with Zend_View. It's hard to tell without seeing your code, but my guess is that findAll() is using the Zend_Table_Db find() function incorrectly.
To my knowledge, the only place that throws that exception is the find() function on Zend_Db_Table_Abstract.
Perhaps, inside the findAll() function (or in a function it calls) you're doing one of these:
$zendDbTable->find(1,2) //is looking for a compound key
$zendDbTable->find(array(1,2)) //is looking for two rows
When you really want the opposite.
how can I get a object from an array when this array is returned by a function?
class Item {
private $contents = array('id' => 1);
public function getContents() {
return $contents;
}
}
$i = new Item();
$id = $i->getContents()['id']; // This is not valid?
//I know this is possible, but I was looking for a 1 line method..
$contents = $i->getContents();
$id = $contents['id'];
You should use the 2-line version. Unless you have a compelling reason to squash your code down, there's no reason not to have this intermediate value.
However, you could try something like
$id = array_pop($i->getContents())
Keep it at two lines - if you have to access the array again, you'll have it there. Otherwise you'll be calling your function again, which will end up being uglier anyway.
I know this is an old question, but my one line soulution for this would be:
PHP >= 5.4
Your soulution should work with PHP >= 5.4
$id = $i->getContents()['id'];
PHP < 5.4:
class Item
{
private $arrContents = array('id' => 1);
public function getContents()
{
return $this->arrContents;
}
public function getContent($strKey)
{
if (false === array_key_exists($strKey, $this->arrContents)) {
return null; // maybe throw an exception?
}
return $this->arrContents[$strKey];
}
}
$objItem = new Item();
$intId = $objItem->getContent('id');
Just write a method to get the value by key.
Best regards.