php magic setters - modified child properties - php

Lets say, we have the following PHP class:
class Product {
protected $data = array();
protected $modified = false;
public function __construct($data) {
$this->data = $data;
}
public function & __get($name) {
if(array_key_exists($name, $this->data)) {
return $this->data->$name;
}
$null = null;
return $null;
}
public function __set($name, $value) {
$this->data->$name = $value;
$this->modified = true;
}
}
$obj = new Product([]);
If I now set a value (for example $obj->name = "Name";), the class property $modified is set to true. Thats what I want to achive.
But is it somehow possible, to "track" modifications, if the are done in object values? For example:
$property = new stdClass();
$property->name = "Name of Prop";
$obj = new Product([
"name" => "Name",
"someObject" => $property
]);
// Now here comes the change
$obj->someObject->name = "New Name";
Because with the code above, $obj->modified would be false

I guess, you have to pass the new value to the "set" function. The way you do it just changes the value itself without going into your function where you declared, that "modified" turns true.
Like this maybe?
$obj->someObject->__set($name, 'New Name');

Related

How to use a array in a class which keeps it values and is acessable from outside the class

I am using php 7.4.9 and have a class which reads information from a file. These informations should be all the time availabe from outside the class and it also should possible to modify that array, so that this class can write back these information on request.
I have looked for a while but could not fined a useful solution.
I got the functions working, but the array loose the values from call to call.
Edit 2020/12/12
This is the uses structure of my code
<?php
.......
function show(){
$id3 = ID3::create();
$mp3 = &ID3::$mp3Array;
if($mode == "manual"){
if($file == ""){
return "";
}
$fName = $dir . "/" . $file;
$id3->open($fName);
.......
}else if($mode == "save"){
$fName = $dir . "/" . $file;
$id3->save($fName);
return "Save done!";
}
} // end of show
class ID3{
public static $mp3Array = array();
public static function create(): self {
static $object;
$object = $object ?? new self();
return $object;
}
function open($fName){
$mp3 = self::$mp3Array;
. // $mp3 will be filled
.........
}
function save($fName) {
$mp3 = &ID3::§mp3Array;
error_log("TagSave: ".var_export($mp3, true),0); // is always empty
foreach($mp3 as $key => $value){
........
}
}
} //end of class>
?>
If I try to save the modified array, it is always empty, if show is called again!
I have also implemented the #Logifire proposal 'create'. I got a valid pointer but the arrayis still empty.
Maybe I should point out, that it is web page. The html code sends information (form) back to the php program.
I figured out, that use of global $id3 = NULL; does not work, because the php grogramm will be always called and set the variable again to NULL each time.
I have also implemented the following code on the beginning
<?php
error_log("PHP call",0);
$id3count = 0;
if(array_key_exists("Test_id3",$GLOBALS)){
error_log("GLOBALS[Test_id3] exist!",0);
}else{
error_log("GLOBALS[Test_id3] does not exist!",0);
$GLOBALS['Test_id3'] = "NEW";
}
The $GLOBAL['Test_id3'] never exist, if the programm will be called!
I got the functions working, but the array loose the values from call to call.
As I understand you, your setup is not a long running app, you can not keep state between requests (calls).
But if you are aware of that, the issue may be you have a new instance of the class each time you call it within the same request flow, you may use a singleton if this is the case. I suggest using accessors in your class.
class MyDataList {
private array $my_array = [];
private function __construct()
{
}
public static function create(): self {
static $object;
$object = $object ?? new self();
return $object;
}
public function setArray(array $new_array): void {
$this->my_array = $new_array;
// open, write, close file..
}
public function getArray(): array {
return $this->my_array;
}
}
$my_data_list = MyDataList::create();
Based on your edited question (2020/12/12), I extended the example code:
class MyDataList {
private array $my_array = [];
private $file_path = '';
private function __construct()
{
}
public static function create(string $file_path): self {
static $object;
if ($object === null) {
$object = new self();
$stringified = file_get_contents($file_path) ?: '';
$array = json_decode($stringified, true) ?: [];
$object->file_path = $file_path;
$object->my_array = $array;
}
return $object;
}
public function setArray(array $new_array): void {
$this->my_array = $new_array;
$stringified = json_encode($new_array);
file_put_contents($this->file_path, $stringified);
}
public function getArray(): array {
return $this->my_array;
}
}
$my_data_list = MyDataList::create('/path/to/file');
Note: Be aware, you need to apply error handling
Comment answers:
Is the filepath connected to the array?
Well, you will write your data as JSON to a file each time you "modify" the array via the setArray()
Does it means, that the array is stored into a file and read out each time I try to connect again?
For each request you call create() it will instantiate the internal state of the array based on the stored data in the file. ATM. The file_get_contents call may have been wrapped and only called if the $object was not instantiated. (Now updated in the example)
So I have to call setArray($array); to save the data. I was looking for a soluting to keep the data without an management to save and read the array. Is this not possible with PHP?
Maybe you want to use a session variable to store your data? But it is individual per user and not long lived data - Link: https://www.php.net/manual/en/reserved.variables.session.php
In a standard PHP setup you can not have data/state between requests, but there are solution like Swoole which makes PHP a long running app: https://www.php.net/manual/en/book.swoole.php
I need a possibility to modify the array directly.
Is it a reference to the array you want? https://3v4l.org/OsBC6
class MyDataList {
private array $my_array = [];
private function __construct()
{
}
public static function create(): self {
static $object;
$object = $object ?? new self();
return $object;
}
public function setArray(array &$new_array): void {
$this->my_array = &$new_array;
}
public function getArray(): array {
return $this->my_array;
}
}
There is no easy way to do with PHP!
Finally I use the proposal from Logifire, but had to modified it to fullfill my requirements.
I needed more than 1 array.
One array can ibclude binary data values, which json can't handle. So I have to use base64 for the binary data values.
Here my code:
public array $mp3Array = array();
public array $findArray = array();
private $file_dir = "";
public static function create(string $fileDir): self {
static $object;
if ($object === null) {
$object = new self();
$stringified1 = file_get_contents($fileDir."/mp3Array.obj") ?: '';
$array1 = json_decode($stringified1, true) ?: [];
$stringified2 = file_get_contents($fileDir."/findArray.obj") ?: '';
$array2 = json_decode($stringified2, true) ?: [];
$object->file_dir = $fileDir;
$object->mp3Array = $object->arrayDecode($array1);
$object->findArray = $array2;
}
return $object;
}
private function arrayEncode($arr){
$tmp = [];
foreach($arr as $key => $val){
if(is_array($val)){
$tmp[$key] = $this->arrayEncode($val);
}else if ($key == "data"){
$tmp[$key] = base64_encode($val);
}else{
$tmp[$key] = $val;
}
}
return $tmp;
}
private function arrayDecode($arr){
$tmp = [];
foreach($arr as $key => $val){
if(is_array($val)){
$tmp[$key] = $this->arrayDecode($val);
}else if ($key == "data"){
$tmp[$key] = base64_decode($val);
}else{
$tmp[$key] = $val;
}
}
return $tmp;
}
public function setMp3(array $new_array): void {
$this->mp3Array = $new_array;
$stringified = json_encode($new_array);
file_put_contents($this->file_dir."/mp3Array.obj", $stringified);
}
public function saveMp3(): void {
$base64 = $this->arrayEncode($this->mp3Array);
$stringified = json_encode($base64);
file_put_contents($this->file_dir."/mp3Array.obj", $stringified);
}
public function setFind(array $new_array): void {
$this->findArray = $new_array;
$stringified = json_encode($new_array);
file_put_contents($this->file_dir."/findArray.obj", $stringified);
}
public function saveFind(): void {
$stringified = json_encode( $this->findArray);
file_put_contents($this->file_dir."/findArray.obj", $stringified);
}

Sharing array inside object through classes in php

My problem is that I have an object shared through two classes that contains an array inside of it and along the script, someone will request some of the classes the value and a foreach loop will change such value and I want this change to affect every reference of the value.
class bar {
protected $obj;
function __construct(&$obj) {
$this->obj = $obj;
}
public function output() {
print_r($this->obj->value);
}
}
class foo {
protected $obj;
function __construct(&$obj) {
$this->obj = $obj;
}
public function val() {
$result = array();
foreach($this->obj->value as $it){
$result[] = $it;
}
return $result;
}
}
// Shared Object
$obj = new stdClass();
// Default value
$obj->value = array('teste', 'banana', 'maca');
// Class 1
$bar = new bar($obj);
// Class 2
$foo = new foo($obj);
// Someone requests from class 2 the values and changes it
$new = $foo->val();
$new[] = 'abc';
// Class 1 outputs the value
$bar->output(); // this will print the default value. I want this to also have 'abc' value.
The main problem, is that you are building a new array at foo:val, you must return the original object to be modified.
I suggest use ArrayObject, have the same behavior of array but is a object, then always is passed by reference.
<?php
class MyArrayObject extends ArrayObject {
public function replace(Array $array)
{
foreach($this->getArrayCopy() as $key => $value) {
$this->offsetUnset($key);
}
foreach ($array as $key => $value) {
$this[$key] = $value;
}
}
}
class bar {
protected $obj;
function __construct(MyArrayObject $obj) {
$this->obj = $obj;
}
public function output() {
print_r($this->obj);
}
}
class foo {
protected $obj;
function __construct(MyArrayObject $obj) {
$this->obj = $obj;
}
public function val() {
$result = array('foo', 'bar');
$this->obj->replace($result);
return $this->obj;
}
}
// Shared Object
$obj = new MyArrayObject(array('teste', 'banana', 'maca'));
// Class 1
$bar = new bar($obj);
// Class 2
$foo = new foo($obj);
// Someone requests from class 2 the values and changes it
$new = $foo->val();
$new[] = 'abc';
// Class 1 outputs the value
$bar->output(); // this will print the default value. I want this to also
var_dump($obj);

Changing an object's type after it has been instantiated in PHP

We have a system where we have reason to instantiate an object before we know what its specific type is. For example, we wish to instantiate a class "media" before we know whether the final class will be "book" or "cd".
Here is what we are doing. We instantiate the "media" object, and once we know what type of media it is, we instantiate a "book", passing in the media object.
class Book extends Media
{
public function __construct( $parent )
{
$vars = get_object_vars( $parent );
foreach( $vars as $key => $value )
$this->$key = $value;
}
}
//elsewhere
$item = new Media();
$item->setPrice( $price );
//other code, figure out the item type
$item = new Book( $item );
Is there a better way to do something like this? Is this dynamic polymorphism?
In case u really can't determine what type of object is i can recommend u factory pattern. With this pattern u has only one entry point and this helps u to get yr code simpler and readable.
Short example:
class ObjectFactory {
public static function factory($object, $objectType)
{
$result = false;
switch ($objectType) {
case 'book':
$result = new Book;
$result->setParams($object->getParams());
break;
case 'otherType':
$result = new OtherType;
$result->setParams($object->getParams());
// or
$result->setParamsFromObject($object);
break;
... //etc
}
return $result;
}
}
class Book extends MediaAbstract
{
public function __set($name, $value)
{
$this->_params($name, $value);
}
public function __get($name)
{
return $this->_params[$value];
}
public function setParams(array $params)
{
$this->_params = $params;
return $this;
}
public function getParams()
{
return $this->_params;
}
// in case u want store params as properties
public function setParamsFromObject($object)
{
$vars = get_object_vars($object);
foreach ($vars as $key => $value) {
$this->$key = $value;
}
return $this;
}
}
$media = new Media;
$media->setParams($params)
// some stuff
//...
$book = ObjectFactory::factory($media, $objectType);

Recommend on how to set the php object more lazily?

Here is the way I do:
$aNewObject = new MyObj();
$aNewObject->set_id($row->id);
$aNewObject->set_user_id($row->user_id);
$aNewObject->set_title($row->title);
$aNewObject->set_url($row->url);
$aNewObject->set_description($row->description);
$aNewObject->set_status($row->status);
as you can see, I follow a name convention, which the object and the data base field is 100% match, I think there should have a way to help me to do it lazier, any recommendation?
You can get even lazier by only writing
$aNewObject = new MyObj($row);
and having a constructor that sets the object's properties based on the contents of $row
You could do the setting dynamically by iterating over the fields (if that is meant by lazier):
$fields = array('user_id', ...);
foreach($fields as $field)
{
$setter = "set_{$field}";
$aNewObject->$setter($row->{$field});
}
It depends then where you want to place that code. Either just inline, as part of a function of MyObj (importRow($row)) or in a global helper function that always calls all setters matching object properties.
Within the class constructor:
$aNewObject = new MyObj($row);
class MyObj
{
public function __construct($row = null)
{
...
$this->importRow($row);
}
public function importRow($row = null)
{
if (null === $row)
return;
foreach($row as $field => $value)
{
$setter = "set_{$field}";
$this->$setter($value);
}
}
...
}
To prevent duplicate code across different classes (missing traits support in PHP < 5.4), a global static function or object can do it:
$aNewObject = new MyObj();
new Setter($aNewObject, $row);
# or
$aNewObject = Setter::fill('MyObj', $row);
class Setter
{
private $object;
public function __construct($class, $data)
{
// works on classnames or objects
if (is_string($class))
$object = new $class();
else
$object = $class;
$this->object = $this->import($object, $data);
}
private function import($object, $data)
{
foreach($data as $field => $value)
{
$setter = "set_{$field}";
$object->$setter($value);
}
return $object;
}
public function getObject()
{
return $this->object;
}
public static function fill($class, $data)
{
$self = new __CLASS__($class, $data);
return $self->getObject();
}
}
Lazier, it is not recommended, because it will make it hard to maintain by other programmers.
If you still want to do that, you should do with PHP reflection: http://www.php.net/manual/en/intro.reflection.php
or as #hakre answered.

Can you create instance properties dynamically in PHP?

Is there any way to create all instance properties dynamically? For example, I would like to be able to generate all attributes in the constructor and still be able to access them after the class is instantiated like this: $object->property. Note that I want to access the properties separately, and not using an array; here's an example of what I don't want:
class Thing {
public $properties;
function __construct(array $props=array()) {
$this->properties = $props;
}
}
$foo = new Thing(array('bar' => 'baz');
# I don't want to have to do this:
$foo->properties['bar'];
# I want to do this:
//$foo->bar;
To be more specific, when I'm dealing with classes that have a large number of properties, I would like to be able to select all columns in a database (which represent the properties) and create instance properties from them. Each column value should be stored in a separate instance property.
Sort of. There are magic methods that allow you to hook your own code up to implement class behavior at runtime:
class foo {
public function __get($name) {
return('dynamic!');
}
public function __set($name, $value) {
$this->internalData[$name] = $value;
}
}
That's an example for dynamic getter and setter methods, it allows you to execute behavior whenever an object property is accessed. For example
print(new foo()->someProperty);
would print, in this case, "dynamic!" and you could also assign a value to an arbitrarily named property in which case the __set() method is silently invoked. The __call($name, $params) method does the same for object method calls. Very useful in special cases. But most of the time, you'll get by with:
class foo {
public function __construct() {
foreach(getSomeDataArray() as $k => $value)
$this->{$k} = $value;
}
}
...because mostly, all you need is to dump the content of an array into correspondingly named class fields once, or at least at very explicit points in the execution path. So, unless you really need dynamic behavior, use that last example to fill your objects with data.
This is called overloading
http://php.net/manual/en/language.oop5.overloading.php
It depends exactly what you want. Can you modify the class dynamically? Not really. But can you create object properties dynamically, as in one particular instance of that class? Yes.
class Test
{
public function __construct($x)
{
$this->{$x} = "dynamic";
}
}
$a = new Test("bar");
print $a->bar;
Outputs:
dynamic
So an object property named "bar" was created dynamically in the constructor.
Yes, you can.
class test
{
public function __construct()
{
$arr = array
(
'column1',
'column2',
'column3'
);
foreach ($arr as $key => $value)
{
$this->$value = '';
}
}
public function __set($key, $value)
{
$this->$key = $value;
}
public function __get($value)
{
return 'This is __get magic '.$value;
}
}
$test = new test;
// Results from our constructor test.
var_dump($test);
// Using __set
$test->new = 'variable';
var_dump($test);
// Using __get
print $test->hello;
Output
object(test)#1 (3) {
["column1"]=>
string(0) ""
["column2"]=>
string(0) ""
["column3"]=>
string(0) ""
}
object(test)#1 (4) {
["column1"]=>
string(0) ""
["column2"]=>
string(0) ""
["column3"]=>
string(0) ""
["new"]=>
string(8) "variable"
}
This is __get magic hello
This code will set dynamic properties in the constructor which can then be accessed with $this->column. It's also good practice to use the __get and __set magic methods to deal with properties that are not defined within the class. More information them can be found here.
http://www.tuxradar.com/practicalphp/6/14/2
http://www.tuxradar.com/practicalphp/6/14/3
You can use an instance variable to act as a holder for arbitrary values and then use the __get magic method to retrieve them as regular properties:
class My_Class
{
private $_properties = array();
public function __construct(Array $hash)
{
$this->_properties = $hash;
}
public function __get($name)
{
if (array_key_exists($name, $this->_properties)) {
return $this->_properties[$name];
}
return null;
}
}
Why is every example so complicated?
<?php namespace example;
error_reporting(E_ALL | E_STRICT);
class Foo
{
// class completely empty
}
$testcase = new Foo();
$testcase->example = 'Dynamic property';
echo $testcase->example;
Here is simple function to populate object members without making class members public.
It also leaves constructor for your own usage, creating new instance of object without invoking constructor! So, your domain object doesn't depend on database!
/**
* Create new instance of a specified class and populate it with given data.
*
* #param string $className
* #param array $data e.g. array(columnName => value, ..)
* #param array $mappings Map column name to class field name, e.g. array(columnName => fieldName)
* #return object Populated instance of $className
*/
function createEntity($className, array $data, $mappings = array())
{
$reflClass = new ReflectionClass($className);
// Creates a new instance of a given class, without invoking the constructor.
$entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className));
foreach ($data as $column => $value)
{
// translate column name to an entity field name
$field = isset($mappings[$column]) ? $mappings[$column] : $column;
if ($reflClass->hasProperty($field))
{
$reflProp = $reflClass->getProperty($field);
$reflProp->setAccessible(true);
$reflProp->setValue($entity, $value);
}
}
return $entity;
}
/******** And here is example ********/
/**
* Your domain class without any database specific code!
*/
class Employee
{
// Class members are not accessible for outside world
protected $id;
protected $name;
protected $email;
// Constructor will not be called by createEntity, it yours!
public function __construct($name, $email)
{
$this->name = $name;
$this->emai = $email;
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getEmail()
{
return $this->email;
}
}
$row = array('employee_id' => '1', 'name' => 'John Galt', 'email' => 'john.galt#whoisjohngalt.com');
$mappings = array('employee_id' => 'id'); // Employee has id field, so we add translation for it
$john = createEntity('Employee', $row, $mappings);
print $john->getName(); // John Galt
print $john->getEmail(); // john.galt#whoisjohngalt.com
//...
P.S. Retrieving data from object is similar, e.g. use $reflProp->setValue($entity, $value);
P.P.S. This function is heavily inspired by Doctrine2 ORM which is awesome!
class DataStore // Automatically extends stdClass
{
public function __construct($Data) // $Data can be array or stdClass
{
foreach($Data AS $key => $value)
{
$this->$key = $value;
}
}
}
$arr = array('year_start' => 1995, 'year_end' => 2003);
$ds = new DataStore($arr);
$gap = $ds->year_end - $ds->year_start;
echo "Year gap = " . $gap; // Outputs 8
You can:
$variable = 'foo';
$this->$variable = 'bar';
Would set the attribute foo of the object it's called on to bar.
You can also use functions:
$this->{strtolower('FOO')} = 'bar';
This would also set foo (not FOO) to bar.
Extend stdClass.
class MyClass extends stdClass
{
public function __construct()
{
$this->prop=1;
}
}
I hope this is what you need.
This is really complicated way to handle this kind of rapid development. I like answers and magic methods but in my opinion it is better to use code generators like CodeSmith.
I have made template that connect to database, read all columns and their data types and generate whole class accordingly.
This way I have error free (no typos) readable code. And if your database model changes run generator again... it works for me.
If you really really must do it, the best way is to overload an ArrayObject, that allows to maintain iteration support (foreach) that will still loop through all your properties.
I note that you said "without using an array", and I just want to assure you that that while technically an array is being used in the background, you NEVER HAVE TO SEE IT. You access all properties via ->properyname or foreach ($class in $name => $value).
Here is a sample I was working on yesterday, note this is also STRONGLY TYPED. So properties that are marked "integer" will throw an error if you try and supply a "string".
You can remove that of course.
There is also an AddProperty() member function, although it is not demonstrated in the example. That will allow you to add properties later.
Sample usage:
$Action = new StronglyTypedDynamicObject("Action",
new StrongProperty("Player", "ActionPlayer"), // ActionPlayer
new StrongProperty("pos", "integer"),
new StrongProperty("type", "integer"),
new StrongProperty("amount", "double"),
new StrongProperty("toCall", "double"));
$ActionPlayer = new StronglyTypedDynamicObject("ActionPlayer",
new StrongProperty("Seat", "integer"),
new StrongProperty("BankRoll", "double"),
new StrongProperty("Name", "string"));
$ActionPlayer->Seat = 1;
$ActionPlayer->Name = "Doctor Phil";
$Action->pos = 2;
$Action->type = 1;
$Action->amount = 7.0;
$Action->Player = $ActionPlayer;
$newAction = $Action->factory();
$newAction->pos = 4;
print_r($Action);
print_r($newAction);
class StrongProperty {
var $value;
var $type;
function __construct($name, $type) {
$this->name = $name;
$this->type = $type;
}
}
class StronglyTypedDynamicObject extends ModifiedStrictArrayObject {
static $basic_types = array(
"boolean",
"integer",
"double",
"string",
"array",
"object",
"resource",
);
var $properties = array(
"__objectName" => "string"
);
function __construct($objectName /*, [ new StrongProperty("name", "string"), [ new StrongProperty("name", "string"), [ ... ]]] */) {
$this->__objectName = $objectName;
$args = func_get_args();
array_shift($args);
foreach ($args as $arg) {
if ($arg instanceof StrongProperty) {
$this->AddProperty($arg->name, $arg->type);
} else {
throw new Exception("Invalid Argument");
}
}
}
function factory() {
$new = clone $this;
foreach ($new as $key => $value) {
if ($key != "__objectName") {
unset($new[$key]);
}
}
// $new->__objectName = $this->__objectName;
return $new;
}
function AddProperty($name, $type) {
$this->properties[$name] = $type;
return;
if (in_array($short_type, self::$basic_types)) {
$this->properties[$name] = $type;
} else {
throw new Exception("Invalid Type: $type");
}
}
public function __set($name, $value) {
self::sdprintf("%s(%s)\n", __FUNCTION__, $name);
$this->check($name, $value);
$this->offsetSet($name, $value);
}
public function __get($name) {
self::sdprintf("%s(%s)\n", __FUNCTION__, $name);
$this->check($name);
return $this->offsetGet($name);
}
protected function check($name, $value = "r4nd0m") {
if (!array_key_exists($name, $this->properties)) {
throw new Exception("Attempt to access non-existent property '$name'");
}
$value__objectName = "";
if ($value != "r4nd0m") {
if ($value instanceof StronglyTypedDynamicObject) {
$value__objectName = $value->__objectName;
}
if (gettype($value) != $this->properties[$name] && $value__objectName != $this->properties[$name]) {
throw new Exception("Attempt to set {$name} ({$this->properties[$name]}) with type " . gettype($value) . ".$value__objectName");
}
}
}
}
class ModifiedStrictArrayObject extends ArrayObject {
static $debugLevel = 0;
/* Some example properties */
static public function StaticDebug($message) {
if (static::$debugLevel > 1) {
fprintf(STDERR, "%s\n", trim($message));
}
}
static public function sdprintf() {
$args = func_get_args();
$string = call_user_func_array("sprintf", $args);
self::StaticDebug("D " . trim($string));
}
protected function check($name) {
if (!array_key_exists($name, $this->properties)) {
throw new Exception("Attempt to access non-existent property '$name'");
}
}
//static public function sget($name, $default = NULL) {
/******/ public function get ($name, $default = NULL) {
self::sdprintf("%s(%s)\n", __FUNCTION__, $name);
$this->check($name);
if (array_key_exists($name, $this->storage)) {
return $this->storage[$name];
}
return $default;
}
public function offsetGet($name) {
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
$this->check($name);
return call_user_func_array(array(parent, __FUNCTION__), func_get_args());
}
public function offsetSet($name, $value) {
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
$this->check($name);
return call_user_func_array(array(parent, __FUNCTION__), func_get_args());
}
public function offsetExists($name) {
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
$this->check($name);
return call_user_func_array(array(parent, __FUNCTION__), func_get_args());
}
public function offsetUnset($name) {
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
$this->check($name);
return call_user_func_array(array(parent, __FUNCTION__), func_get_args());
}
public function __toString() {
self::sdprintf("%s(%s)\n", __FUNCTION__, $name);
foreach ($this as $key => $value) {
$output .= "$key: $value\n";
}
return $output;
}
function __construct($array = false, $flags = 0, $iterator_class = "ArrayIterator") {
self::sdprintf("%s(%s)\n", __FUNCTION__, implode(",", func_get_args()));
parent::setFlags(parent::ARRAY_AS_PROPS);
}
}
After reading #Udo 's answer. I've come up with the following pattern, that doesn't bloat a class instance with what-ever items that is in your constructor array argument but still let you type less and easily add new properties to the class.
class DBModelConfig
{
public $host;
public $username;
public $password;
public $db;
public $port = '3306';
public $charset = 'utf8';
public $collation = 'utf8_unicode_ci';
public function __construct($config)
{
foreach ($config as $key => $value) {
if (property_exists($this, $key)) {
$this->{$key} = $value;
}
}
}
}
Then you can pass arrays like:
[
'host' => 'localhost',
'driver' => 'mysql',
'username' => 'myuser',
'password' => '1234',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'db' => 'key not used in receiving class'
]

Categories