I have a subclass of a SimpleXMLElement that I want to a class properties to that will define some default values for an attribute of created child-nodes.
In order to set this values, I attempted to override addChild with the following method:
public function addChild($name, $value = '')
{
$child = parent::addChild($name, $enc_val);
error_log(print_r($this->default_link_type, true));
$child->set_default_val($this->default_val);
return $child;
}
The problem is the expression: $this->default_val doesn't return the value of the property, but instead creates a new empty child object and returns it.
Can anyone think of a way of overriding this behvior for certain properties? Or think of any hack that will allow me to have essentially a global state for all of these SimpleXMLElement subclasses that doesn't involve writing to a file, key-value store etc.?
EDIT: The below doesn't fully work because now when do call asXML() all the elements have the config property as a child node. So I'm still after a solution.
I have finally found the answer by experimenting with various classes/methods in PHP's Reflection API.
You can add this to the SimpleXMLElement sublass to a get the value of a property of the current object:
protected function get_property($name)
{
$rc = new ReflectionClass($this);
$props = $rc->getDefaultProperties();
if (!isset($props[$name])) {
throw new InvalidArgumentException(
'$name does not hold the value of a valid property!'
);
}
return $props[$name];
}
Using methods such as ReflectionClass::getProperty() will not work.
Related
I'm writing a method that copies an object. Instead of manually setting each property manually, it would be more robust to just loop over the original object's properties...
//Booo
$new->name = $old->name;
$new->color = $old->color;
...
//Oh yeah...
foreach ($old as $prop=>$val){
$new->$prop = $val;
}
unset $new->id;
It appears that CakePHP entities cannot be iterated over in this way. I tried using $old->toArray(), which basically works... but has the drawback of converting all the associations to arrays also, which is screwing this up for me down stream.
How do I loop over the $old properties without converting all the data types?
Update:
Mark brought to my attention the existence of a __clone() method. Sounds like it does exactly what I need but I'm still figuring out how to use it.
You can use $entity->visualProperties()
foreach($old->visualProperties() as $property) {
if($new->has($property))
$new->set($property, $old->get($property));
After looking at this for a while, and discovering there is no __clone() function for entities, at least in 3.8, I have worked out how to do it, with the hint from DouglasSantos :
//Find out the entity classname
$classname = get_class($entity);
//Instanciate a new object of that class
$clone = new $classname;
//Use visibleProperties to clone it
foreach($entity->visibleProperties() as $property)
if($clone->has($property))
$clone->set($property, $entity->get($property));
Of course you could combine the first 2 lines into one line, but I have split it out for clarity.
UPDATE: I have discovered if you use the has->($property) check it will skip many of the fields. So the corrected answer is :
//Find out the entity classname
$classname = get_class($entity);
//Instanciate a new object of that class
$clone = new $classname;
//Use visibleProperties to clone it
foreach($entity->visibleProperties() as $property)
$clone->$property = $entity->$property;
It is actually much easier to use the Table Object:
// Assuming your model is called "Documents"
// If you are in the Controller, you can just use `$this->Documents`
instead of fetching the Table from the Registry
use Cake\ORM\TableRegistry;
$table = TableRegistry::getTableLocator()->get('Documents');
// newEntity() creates a new Entity from an array of data
$documentCopy = $table->newEntity(
// extract() extracts the given properties as an associative array
$document->extract(
// getVisible() will get all visible properties as an array
$document->getVisible()
)
);
I have the following issue:
The current code of an application I'm working on contains a very large number of definitions like this:
$obj = new stdClass();
$obj->a->b = "something";
This results in: PHP Strict Standards: Creating default object from empty value in [somewhere].
The correct form would be:
$obj = new stdClass();
$obj->a = new stdClass();
$obj->a->b = "something";
Now the problem: Replacing this throughout the code would take ages (consider thousands of cases, with conditions, etc.).
So I was thinking of replacing stdClass with a custom object (this would be a simple replace in code), creating a setter for it that verifies if the variable property is an object, and defines it as object if it is before setting the second property.
So we get:
class MockObject() {
public function __set($property, $value) {
// Do stuff here
}
}
$obj = new MockObject();
$object->a->b = "something";
The problem is that when executing $object->a->b = "something"; setter is not called (because you don't actually set the a property, but the b property).
Is there any way around this? Or is other solution possible?
Note: Explicitly calling the __set() method is not a solution since it would be the same as defining properties as stdClass().
You know about the magic setter.
Use a magic getter also.
If it wants to get a var that does not exists: create one (in an array or something like that) that is an instance of that class.
Why don't you initialize your b variable in the constructor of the A class ?
public function __construct()
{
$this->b = new B();
}
Is it possible to check properties from a PHP stdClass? I have some models which are being generated as an stdClass. When using them I would like to check if the properties I'm calling exist in some kind of Core-class. I've noticed __get is ignored by the stdClass...
How can properties from a stdClass be checked if they exist in the object?
StdClass objects contain only porperties, not code. So you can't code anything from "within" them. So you need to work around this "shortcomming". Depending on what generates these classes this can be done by "overloading" the data (e.g. with a Decorator) providing the functionality you've looking for:
class MyClass
{
private $subject;
public function __construct(object $stdClass)
{
$this->subject = $stdClass;
}
public function __get($name)
{
$exists = isset($this->subject->$name);
#...
}
}
$myModel = new MyClass($model);
Use get_object_vars() to iterate through the stdClass object, then use the property_exists() function to see if the current property exists in the parent class.
Just cast it to an array
$x = (array) $myStdClassObject;
Then you can use all the common array functions
Quick one:
Is there any way to enforce types for variadic functions in PHP? I'm assuming not, however maybe I've missed something.
As of now, I'm just forcing a single required argument of the needed type, and iterating to check the rest.
public function myFunction(MyClass $object){
foreach(func_get_args() as $object){
if(!($object instanceof MyClass)){
// throw exception or something
}
$this->_objects[] = $object;
}
}
Any better solutions?
Purpose:
A container object that acts as an iterated list of the child objects, with some utility functions. calling it with a variadic constructor would be something like this:
// returned directly from include
return new MyParent(
new MyChild($params),
new MyChild($params),
new MyChild($params)
);
The other option could be an addChild method chain:
$parent = new MyParent;
return $parent
->addChild(new MyChild($params))
->addChild(new MyChild($params))
->addChild(new MyChild($params));
The children take several arguments to their constructor as well, so I'm trying to balance between legibility and processing expense.
This is now possible with PHP 5.6.x, using the ... operator (also known as splat operator in some languages):
Example:
function addDateIntervalsToDateTime( DateTime $dt, DateInterval ...$intervals )
{
foreach ( $intervals as $interval ) {
$dt->add( $interval );
}
return $dt;
}
Well I would say it depends on the number of arguments :) There is nothing like a list (all arguments 1-n as MyClass [before PHP 5.6, for PHP 5.6+ see Variadic functions]), it's more that you need to write each argument (as in your question) and it's allowed to send more but only the ones defined will be checked.
However you can define more and make them optional. Hence why I just wrote it depends on the number of arguments:
public function myFunction(MyClass $object, MyClass $object2=null, MyClass $object3=null, MyClass $object4=null, ...){
foreach(func_get_args() as $object){
if(null === $object){
// throw exception or something
}
$this->_objects[] = $object;
}
}
With such an example, PHP would have thrown the exception already when the argument is not NULL and not MyClass (Passing NULL if given as default value, is possible to pass as well). Optional parameter should not be part of the return value of func_get_args(). I mean, if you don't pass the third argument (here named $object3) it won't appear in the array returned by func_get_args().
No idea if that is more practicable for you than your the code in the question.
If you face more such situations in your code, you can create some sort of helper to validate the input of function calls and delegate to throw the exceptions with nice error message from the helper. That will prevent duplicate code and will give more comfort in development as you can make the exception notices nicer as you write them for multiple cases.
According to your new feedback, if you want first of all the interpreter let the work of checking, a little iteration and the addMember function would do it:
class MyParent {
private $children = array();
public function __construct() {
foreach(func_get_args() as $object) $this->addChild($object);
}
public function addChild(MyChild $object) {
$this->children[] = $object;
}
}
Instantiating the object with a wrong type of object in any of the list will prevent the Collection to be instantiated.
you can then just do this:
// returned directly from include
return new MyParent(
new MyChild($params),
new MyChild($params),
new MyChild($params)
);
I have a custom class object in PHP named product:
final class product
{
public $id;
public $Name;
public $ProductType;
public $Category;
public $Description;
public $ProductCode;
}
When passing an object of this class to my Data Access Layer I need to cast the object passed into a type of the product class so I can speak to the properties within that function. Since type casting in PHP works only with basic types what is the best solution to cast that passed object?
final class productDAL
{
public function GetItem($id)
{
$mySqlConnection = mysql_connect('localhost', 'username', 'password');
if (!$mySqlConnection) { trigger_error('Cannot connect to MySql Server!'); return; }
mysql_select_db('databaseName');
$rs = mysql_query("SELECT * FROM tblproduct WHERE ID='$id';");
$returnObject = mysql_fetch_object($rs, 'product');
return $returnObject;
}
public function SaveItem($objectToSave, $newProduct = false)
{
$productObject = new product();
$productObject = $objectToSave;
echo($objectToSave->Name);
$objectToSave->ID;
}
}
Right now I am creating a new object cast as a type of product and then setting it equal to the object passed to the function. Is there a better way of accomplishing this task? Am I going about the wrong way?
EDITED FOR CLARITY - ADD FULL PRODCUTDAL CLASS
You don't need to cast the object, you can just use it as if it was a product.
$name = $objectToSave->Name;
I´m not sure what you are trying to achieve, but if $objectToSave is already of class product:
You can simply call $objectToSave->SaveItem() (assuming SaveItem() is part of the product class) and access it´s properties in the function like $this->Name, etc.;
In your code $productObject and $objectToSave will hold a reference to the same object.
Type casts in PHP are done like this:
$converted = (type) $from;
Note, that this won't work if the object types are not compatible (if for example $form happens to be a string or object of mismatching type).
But usual solution (called Active Record pattern, present for example in Zend Framework) is to have a base class for a database item called Row. Individual items (for example the class product from your sample) then inherit from this class.
Typical ZF scenario:
$table = new Product_Table();
$product = $table->find($productId); // load the product with $productId from DB
$product->someProperty = $newPropertyValue;
$product->Save(); // UPDATE the database
Which is IMO much better than your solution.
EDIT:
You can't cast between two unrelated objects, it is not possible.
If you want to use the DAL like this, skip the "product" object and go for simple associative array. You can enumerate over its members with foreach, unlike object's properties (you could use reflection, but that's overkill).
My recommendation: Go for the Active Record pattern (it is easy to implement with magic methods). It will save you a lot of trouble.
Currently, you are creating a new Product, then discarding it immediately (as its reference is replaced by $objectToSave.) You will need to copy its properties one by one, I regret.
foreach (get_object_vars($objectToSave) as $key => $value)
{
$product->$key = $value;
}
(If the properties of $objectToSave are private, you will need to a expose a method to_array() that calls get_object_vars($this).)