I see there are about a hundred different questions on here for Creating default object from empty value. None of them really seem to help with my issue.
I am assigning a child object of the same class in a method. This triggers the Creating default object from empty value error.
class myClass {
function __construct($rootName, $allowHTML = false, $endTag = true) {
$this->rootElement = $rootName;
$this->elements = array();
$this->attributes = array();
$this->value = "";
$this->allowHTML = $allowHTML;
$this->endTag = $endTag;
$this->styles = array();
$this->childID = 0;
}
// ... OTHER METHODS HERE (ALL PROPERTIES DECLARED)
function assignElement(&$theElement) {
// Get the index.
$index = count($this->elements);
// Assign the element.
$this->elements[$index] = $theElement;
if (get_class($theElement) == get_class($this)) {
$this->elements[$index]->childID = $index;
}
// Return the node.
return $this->elements[$index];
}
}
The error occurs on $this->elements[$index]->childID = $index;. How do I handle this properly?
Seems like you are passing NULL to the assignElement. get_class called with no arguments or null as argument, returns class of the object where it is defined, so your if conditions is true for null values. You should use is_object first.
Related
I have the function/method below inside a class that I'm creating and I'm just wondering what's the best way to handle empty/null arguments.
For example, in the following example, if I wanted to just set just the category when calling the function, I would need to use:
$data = $class->get_top_headlines( null, 'technology' );
Is there any way of calling the function more efficiently? I know I could pass the arguments as an array instead, but just wondered if there's any way of doing something like:
$data = $class->get_top_headlines( $category='technology' ); and automatically leaving the other arguments as their default of null?
public function get_top_headlines( $query=null, $category=null, $country=null, $sources=null, $page_size=null, $page=null ){
$url = $this->api_url . $this->endpoint_top_headlines;
$params = array();
if ( $query !== null ){
$params['q'] = urlencode( $query );
}
if ( $category !== null ){
$params['category'] = $category;
}
if ( $country !== null ){
$params['country'] = $country;
}
if ( $sources !== null ){
$params['sources'] = $sources;
}
if ( $page_size !== null ){
$params['pageSize'] = $page_size;
}
if ( $page !== null ){
$params['page'] = $page;
}
$params['apiKey'] = $this->api_key;
$url_query = http_build_query( $params );
$url = $url . '?' . $url_query;
echo $url;
$obj = $this->get_response( $url );
return $obj;
}
Try passing an array, and then using an array_merge
$data = $class->get_top_headlines(['category' => 'technology']);
Then in your function, have an array of defaults, then do your merge.
$settings = array_merge($settings, $passedInArray);
http://php.net/manual/en/function.array-merge.php
I think
(null, 'technology' );
might be less actual coding but a different solution might be to use OOP. You said it's already a method of a class so you could do something like:
$obj = new thatClass;
$obj->technology = $technology;
$obj->get_top_headlines();
in the Class:
Class thatClass{
$technology = null;
$category = null;
$query = null;
//...
public function get_top_headlines(){
if ( $this->query !== null ){
$params['q'] = urlencode( $this->query );
}
if ( $this->category !== null ){
$params['category'] = $this->category;
}
if ( $this->technology !== null ){
$params['technology'] = $this->technology;
}
//method code..
}
//class code..
}
The problem with this approach is if you need to call that same function again passing a different parameter in the same class instance, depending on your application you might need to manually set back to null the previous parameter (now an object attribute)
I would solve this problem by creating a new class or data structure which will encapsulate all the logic of validating and generating the URL and then use it everywhere I need it.
Here's a sample class.
class HeadLineParameters
{
private $params = [];
public function setQuery($query)
{
// validate/transform query data
$this->params['q'] = urlencode($query);
return $this;
}
public function setCategory($category)
{
// validate/transform category data
$this->params['category'] = $category;
return $this;
}
public function generateUrl()
{
return http_build_query( $this->params );
}
}
$params = new HeadLineParameters;
$params->setQuery($query)
->setCategory($category);
You just pass one argument and you know that it's just an instance of HeadLineParameters.
$class->get_top_headlines($params);
This solution doesn't pollute your current class with unnecessary state or fields. It is easy to test, and it has only one job. You can extend it easily, you can set default values, you can also validate it as you like.
Edit: Why you shouldn't add more fields to your current class?
If you add more fields to your current class you'll be breaking the single responsibility principle, and any method of this class can change these fields too. It shouldn't be a problem if these fields really belong there and more methods require them. This is fine if you are using OOP.
I am not sure what other people think about passing associated arrays to functions, but they are hard to handle if you have no documentation available. I have had trouble with them when reading some external code, and most of time I wasn't sure what's the data I was dealing with.
How to find if an object is empty or not in PHP.
Following is the code in which $obj is holding XML data. How can I check if it's empty or not?
My code:
$obj = simplexml_load_file($url);
You can cast to an array and then check if it is empty or not
$arr = (array)$obj;
if (!$arr) {
// do stuff
}
Edit: I didn't realize they wanted to specifically check if a SimpleXMLElement object is empty. I left the old answer below
Updated Answer (SimpleXMLElement):
For SimpleXMLElement:
If by empty you mean has no properties:
$obj = simplexml_load_file($url);
if ( !$obj->count() )
{
// no properties
}
OR
$obj = simplexml_load_file($url);
if ( !(array)$obj )
{
// empty array
}
If SimpleXMLElement is one level deep, and by empty you actually mean that it only has properties PHP considers falsey (or no properties):
$obj = simplexml_load_file($url);
if ( !array_filter((array)$obj) )
{
// all properties falsey or no properties at all
}
If SimpleXMLElement is more than one level deep, you can start by converting it to a pure array:
$obj = simplexml_load_file($url);
// `json_decode(json_encode($obj), TRUE)` can be slow because
// you're converting to and from a JSON string.
// I don't know another simple way to do a deep conversion from object to array
$array = json_decode(json_encode($obj), TRUE);
if ( !array_filter($array) )
{
// empty or all properties falsey
}
Old Answer (simple object):
If you want to check if a simple object (type stdClass) is completely empty (no keys/values), you can do the following:
// $obj is type stdClass and we want to check if it's empty
if ( $obj == new stdClass() )
{
echo "Object is empty"; // JSON: {}
}
else
{
echo "Object has properties";
}
Source: http://php.net/manual/en/language.oop5.object-comparison.php
Edit: added example
$one = new stdClass();
$two = (object)array();
var_dump($one == new stdClass()); // TRUE
var_dump($two == new stdClass()); // TRUE
var_dump($one == $two); // TRUE
$two->test = TRUE;
var_dump($two == new stdClass()); // FALSE
var_dump($one == $two); // FALSE
$two->test = FALSE;
var_dump($one == $two); // FALSE
$two->test = NULL;
var_dump($one == $two); // FALSE
$two->test = TRUE;
$one->test = TRUE;
var_dump($one == $two); // TRUE
unset($one->test, $two->test);
var_dump($one == $two); // TRUE
You can cast your object into an array, and test its count like so:
if(count((array)$obj)) {
// doStuff
}
Imagine if the object is not empty and in a way quite big, why would you waste the resources to cast it to array or serialize it...
This is a very easy solution I use in JavaScript. Unlike the mentioned solution that casts an object to array and check if it is empty, or to encode it into JSON, this simple function is very efficient concerning used resources to perform a simple task.
function emptyObj( $obj ) {
foreach ( $obj AS $prop ) {
return FALSE;
}
return TRUE;
}
The solution works in a very simple manner: It wont enter a foreach loop at all if the object is empty and it will return true. If it's not empty it will enter the foreach loop and return false right away, not passing through the whole set.
Using empty() won't work as usual when using it on an object, because the __isset() overloading method will be called instead, if declared.
Therefore you can use count() (if the object is Countable).
Or by using get_object_vars(), e.g.
get_object_vars($obj) ? TRUE : FALSE;
Another possible solution which doesn't need casting to array:
// test setup
class X { private $p = 1; } // private fields only => empty
$obj = new X;
// $obj->x = 1;
// test wrapped into a function
function object_empty( $obj ){
foreach( $obj as $x ) return false;
return true;
}
// inline test
$object_empty = true;
foreach( $obj as $object_empty ){ // value ignored ...
$object_empty = false; // ... because we set it false
break;
}
// test
var_dump( $object_empty, object_empty( $obj ) );
there's no unique safe way to check if an object is empty
php's count() first casts to array, but casting can produce an empty array, depends by how the object is implemented (extensions' objects are often affected by those issues)
in your case you have to use $obj->count();
http://it.php.net/manual/en/simplexmlelement.count.php
(that is not php's count http://www.php.net/count )
in PHP version 8
consider you want to access a property of an object, but you are not sure that the object itself is null or not and it could cause error. in this case you can use Nullsafe operator that introduced in php 8 as follow:
$country = $session?->user?->getAddress()?->country;
If you cast anything in PHP as a (bool), it will tell you right away if the item is an object, primitive type or null. Use the following code:
$obj = simplexml_load_file($url);
if (!(bool)$obj) {
print "This variable is null, 0 or empty";
} else {
print "Variable is an object or a primitive type!";
}
If an object is "empty" or not is a matter of definition, and because it depends on the nature of the object the class represents, it is for the class to define.
PHP itself regards every instance of a class as not empty.
class Test { }
$t = new Test();
var_dump(empty($t));
// results in bool(false)
There cannot be a generic definition for an "empty" object. You might argue in the above example the result of empty() should be true, because the object does not represent any content. But how is PHP to know? Some objects are never meant to represent content (think factories for instance), others always represent a meaningful value (think new DateTime()).
In short, you will have to come up with your own criteria for a specific object, and test them accordingly, either from outside the object or from a self-written isEmpty() method in the object.
I was using a json_decode of a string in post request. None of the above worked for me, in the end I used this:
$post_vals = json_decode($_POST['stuff']);
if(json_encode($post_vals->object) != '{}')
{
// its not empty
}
Simply check if object type is null or not.
if( $obj !== null )
{
// DO YOUR WORK
}
count($the_object) > 0 this is working and can be use for array too
Based on this answer from kenorb, here's another one-liner for objects with public vars:
if (!empty(get_object_vars($myObj))) { ... }
Edit: Thanks to #mickmackusa's comment below - below is a more succinct one-liner, since this converts the object to an associative array (of accessible properties), and an empty array is falsy in PHP.
if (get_object_vars($myObj)) { ... }
Just to reiterate - this is for objects with public/accessible variables. Objects with static, private, or protected vars will render false, which may be unexpected. See https://www.php.net/manual/en/function.get-object-vars.php
$array = array_filter($array);
if(!empty($array)) {
echo "not empty";
}
or
if(count($array) > 0) {
echo 'Error';
} else {
echo 'No Error';
}
How $mysettings can be true while we are initializing it with null? is this a method to prevent SQL injection? It would be appreciated if you could explain the code below.
public function __construct($mysettings = null)
{
$this->shop_version = Mage::getVersion();
$this->moduleversion = Mage::getConfig()->getModuleConfig('Messagemodule')->version;
$this->apppid = Mage::getStoreConfig('magemessage/appId');
if (empty($this->apppid)) {
$this->apppid = 'no-appId';
}
$this->connectortype = ($settingvariable = Mage::getStoreConfig('Messagemodule/magemessage/connector', 0)) ? $settingvariable : 'auto';
if ($mysettings) {
$this->connectortype = $mysettings;
}
}
When you specify a default value in a PHP method (including a constructor), that's all it is - a default.
So if you have
class Foo {
public function __construct($mysettings = null) {
...
}
}
then you are providing two ways of constructing the class. You can either call
$foo = new Foo();
with no arguments, in which case $mysettings will be initialised to null. Or you can call
$settings = array('key' => 'value');
$foo = new Foo($settings);
in which case the $settings array will be passed into the new instance. The benefit this provides is that you don't need to provide an empty array to new instances for which you don't need custom settings; you can just omit the argument.
The check if ($mysettings)... in the class ensures that the settings are only used if they are provided - a PHP if statement can operate on lots of different types, not just booleans. In this case, if the variable is null, the condition will evaluate to false.
Have a look at this code:
<?php
function required($something)
{
echo $something;
}
required();
It throws a fatal error, because $something was required, but not passed. https://3v4l.org/fIKB9
Now look here:
<?php
function required($something = 'hello')
{
echo $something;
}
required();
required(' R.Toward');
Which outputs Hello R.Toward https://3v4l.org/nQF8r
So in essence, it is a way of setting a default optional value.
I am have written a helper function to "cleanup" callback variables for input into MySQL. This is the function that I wrote:
public function string($object, $objectPath) {
if (!empty($object->$objectPath) || $object->$objectPath !== '') {
$value = $object->$objectPath;
} else {
return 'NULL';
}
if (!empty($value) || $value != '') {
return "'".str_replace("'","''",$value)."'";
} else {
return 'NULL';
}
}
Now, $object is always an object returned by the call, and $objectPath is always a string to points to a given value. Here's where the problem comes in. This works:
$value = $this->db->string($object, 'foo');
However, this does not work:
$value = $this->db->string($object, 'foo->bar->foo1->bar1');
Whenever $objectPath is more than "one layer" deep, I get the following error from (Amazon's) client library:
Fatal error: Call to undefined method MarketplaceWebServiceOrders_Model_Order::getFoo->Bar() in /path/to/Model.php on line 63
The code block that the error refers to is this:
public function __get($propertyName)
{
$getter = "get$propertyName";
return $this->$getter(); // this is line 63
}
$object is not XML, so I can't use SimpleXMLElement and XPath.
What is the problem with my code? Is it that am I concatenating an object and a string? If so, how can I make that possible? How can I get this function to do what I intended it to do?
By the way, I'm using PHP 5.4.27.
PHP doesn't automatically resolve a string containing multiple path levels to children of an object like you are attempting to do.
This will not work even if $obj contains the child hierarchy you are expecting:
$obj = ...;
$path = 'level1->level2->level3';
echo $obj->$path; // WRONG!
You would need to split up the path and "walk" through the object trying to resolve the final property.
Here is an example based on yours:
<?php
$obj = new stdClass();
$obj->name = 'Fred';
$obj->job = new stdClass();
$obj->job->position = 'Janitor';
$obj->job->years = 4;
print_r($obj);
echo 'Years in current job: '.string($obj, 'job->years').PHP_EOL;
function string($obj, $path_str)
{
$val = null;
$path = preg_split('/->/', $path_str);
$node = $obj;
while (($prop = array_shift($path)) !== null) {
if (!is_object($obj) || !property_exists($node, $prop)) {
$val = null;
break;
}
$val = $node->$prop;
// TODO: Insert any logic here for cleaning up $val
$node = $node->$prop;
}
return $val;
}
Here it is working: http://3v4l.org/9L4gc
With #itsmejodie's help, I finally got a working solution:
public function string($node, $objectPath) {
$value = NULL;
$path = explode('->', $objectPath);
while (($prop = array_shift($path)) !== NULL) {
if (!$node->$prop) {
break;
}
$value = $node->$prop;
$node = $node->$prop;
}
if (is_string($value)) {
return "'".str_replace("'","''",$value)."'";
} else {
return 'NULL';
}
}
The key for me was to see that, as #itsmejodie put it, "PHP doesn't automatically resolve a string containing multiple path levels to children of an object." In a string like, 'foo->bar->foo1->bar2', PHP won't convert the ->'s into the T_OBJECT_OPERATOR, thus appending a string to an object, e.g., $object->foo->bar->foo1->bar2, just won't work.
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.