Best way to do multiple constructors in PHP - php
You can't put two __construct functions with unique argument signatures in a PHP class. I'd like to do this:
class Student
{
protected $id;
protected $name;
// etc.
public function __construct($id){
$this->id = $id;
// other members are still uninitialized
}
public function __construct($row_from_database){
$this->id = $row_from_database->id;
$this->name = $row_from_database->name;
// etc.
}
}
What is the best way to do this in PHP?
I'd probably do something like this:
<?php
class Student
{
public function __construct() {
// allocate your stuff
}
public static function withID( $id ) {
$instance = new self();
$instance->loadByID( $id );
return $instance;
}
public static function withRow( array $row ) {
$instance = new self();
$instance->fill( $row );
return $instance;
}
protected function loadByID( $id ) {
// do query
$row = my_awesome_db_access_stuff( $id );
$this->fill( $row );
}
protected function fill( array $row ) {
// fill all properties from array
}
}
?>
Then if i want a Student where i know the ID:
$student = Student::withID( $id );
Or if i have an array of the db row:
$student = Student::withRow( $row );
Technically you're not building multiple constructors, just static helper methods, but you get to avoid a lot of spaghetti code in the constructor this way.
The solution of Kris is really nice, but I prefer a mix of factory and fluent style:
<?php
class Student
{
protected $firstName;
protected $lastName;
// etc.
/**
* Constructor
*/
public function __construct() {
// allocate your stuff
}
/**
* Static constructor / factory
*/
public static function create() {
return new self();
}
/**
* FirstName setter - fluent style
*/
public function setFirstName($firstName) {
$this->firstName = $firstName;
return $this;
}
/**
* LastName setter - fluent style
*/
public function setLastName($lastName) {
$this->lastName = $lastName;
return $this;
}
}
// create instance
$student= Student::create()->setFirstName("John")->setLastName("Doe");
// see result
var_dump($student);
?>
PHP is a dynamic language, so you can't overload methods. You have to check the types of your argument like this:
class Student
{
protected $id;
protected $name;
// etc.
public function __construct($idOrRow){
if(is_int($idOrRow))
{
$this->id = $idOrRow;
// other members are still uninitialized
}
else if(is_array($idOrRow))
{
$this->id = $idOrRow->id;
$this->name = $idOrRow->name;
// etc.
}
}
As has already been shown here, there are many ways of declaring multiple constructors in PHP, but none of them are the correct way of doing so (since PHP technically doesn't allow it).
But it doesn't stop us from hacking this functionality...
Here's another example:
<?php
class myClass {
public function __construct() {
$get_arguments = func_get_args();
$number_of_arguments = func_num_args();
if (method_exists($this, $method_name = '__construct'.$number_of_arguments)) {
call_user_func_array(array($this, $method_name), $get_arguments);
}
}
public function __construct1($argument1) {
echo 'constructor with 1 parameter ' . $argument1 . "\n";
}
public function __construct2($argument1, $argument2) {
echo 'constructor with 2 parameter ' . $argument1 . ' ' . $argument2 . "\n";
}
public function __construct3($argument1, $argument2, $argument3) {
echo 'constructor with 3 parameter ' . $argument1 . ' ' . $argument2 . ' ' . $argument3 . "\n";
}
}
$object1 = new myClass('BUET');
$object2 = new myClass('BUET', 'is');
$object3 = new myClass('BUET', 'is', 'Best.');
Source: The easiest way to use and understand multiple constructors:
Hope this helps. :)
public function __construct() {
$parameters = func_get_args();
...
}
$o = new MyClass('One', 'Two', 3);
Now $paramters will be an array with the values 'One', 'Two', 3.
Edit,
I can add that
func_num_args()
will give you the number of parameters to the function.
You could do something like this:
public function __construct($param)
{
if(is_int($param)) {
$this->id = $param;
} elseif(is_object($param)) {
// do something else
}
}
As of version 5.4, PHP supports traits. This is not exactly what you are looking for, but a simplistic trait based approach would be:
trait StudentTrait {
protected $id;
protected $name;
final public function setId($id) {
$this->id = $id;
return $this;
}
final public function getId() { return $this->id; }
final public function setName($name) {
$this->name = $name;
return $this;
}
final public function getName() { return $this->name; }
}
class Student1 {
use StudentTrait;
final public function __construct($id) { $this->setId($id); }
}
class Student2 {
use StudentTrait;
final public function __construct($id, $name) { $this->setId($id)->setName($name); }
}
We end up with two classes, one for each constructor, which is a bit counter-productive. To maintain some sanity, I'll throw in a factory:
class StudentFactory {
static public function getStudent($id, $name = null) {
return
is_null($name)
? new Student1($id)
: new Student2($id, $name)
}
}
So, it all comes down to this:
$student1 = StudentFactory::getStudent(1);
$student2 = StudentFactory::getStudent(1, "yannis");
It's a horribly verbose approach, but it can be extremely convenient.
Here is an elegant way to do it. Create trait that will enable multiple constructors given the number of parameters. You would simply add the number of parameters to the function name "__construct". So one parameter will be "__construct1", two "__construct2"... etc.
trait constructable
{
public function __construct()
{
$a = func_get_args();
$i = func_num_args();
if (method_exists($this,$f='__construct'.$i)) {
call_user_func_array([$this,$f],$a);
}
}
}
class a{
use constructable;
public $result;
public function __construct1($a){
$this->result = $a;
}
public function __construct2($a, $b){
$this->result = $a + $b;
}
}
echo (new a(1))->result; // 1
echo (new a(1,2))->result; // 3
Another option is to use default arguments in the constructor like this
class Student {
private $id;
private $name;
//...
public function __construct($id, $row=array()) {
$this->id = $id;
foreach($row as $key => $value) $this->$key = $value;
}
}
This means you'll need to instantiate with a row like this: $student = new Student($row['id'], $row) but keeps your constructor nice and clean.
On the other hand, if you want to make use of polymorphism then you can create two classes like so:
class Student {
public function __construct($row) {
foreach($row as $key => $value) $this->$key = $value;
}
}
class EmptyStudent extends Student {
public function __construct($id) {
parent::__construct(array('id' => $id));
}
}
as stated in the other comments, as php does not support overloading, usually the "type checking tricks" in constructor are avoided and the factory pattern is used intead
ie.
$myObj = MyClass::factory('fromInteger', $params);
$myObj = MyClass::factory('fromRow', $params);
You could do something like the following which is really easy and very clean:
public function __construct()
{
$arguments = func_get_args();
switch(sizeof(func_get_args()))
{
case 0: //No arguments
break;
case 1: //One argument
$this->do_something($arguments[0]);
break;
case 2: //Two arguments
$this->do_something_else($arguments[0], $arguments[1]);
break;
}
}
This question has already been answered with very smart ways to fulfil the requirement but I am wondering why not take a step back and ask the basic question of why do we need a class with two constructors?
If my class needs two constructors then probably the way I am designing my classes needs little more consideration to come up with a design that is cleaner and more testable.
We are trying to mix up how to instantiate a class with the actual class logic.
If a Student object is in a valid state, then does it matter if it was constructed from the row of a DB or data from a web form or a cli request?
Now to answer the question that that may arise here that if we don't add the logic of creating an object from db row, then how do we create an object from the db data, we can simply add another class, call it StudentMapper if you are comfortable with data mapper pattern, in some cases you can use StudentRepository, and if nothing fits your needs you can make a StudentFactory to handle all kinds of object construction tasks.
Bottomline is to keep persistence layer out of our head when we are working on the domain objects.
I know I'm super late to the party here, but I came up with a fairly flexible pattern that should allow some really interesting and versatile implementations.
Set up your class as you normally would, with whatever variables you like.
class MyClass{
protected $myVar1;
protected $myVar2;
public function __construct($obj = null){
if($obj){
foreach (((object)$obj) as $key => $value) {
if(isset($value) && in_array($key, array_keys(get_object_vars($this)))){
$this->$key = $value;
}
}
}
}
}
When you make your object just pass an associative array with the keys of the array the same as the names of your vars, like so...
$sample_variable = new MyClass([
'myVar2'=>123,
'i_dont_want_this_one'=> 'This won\'t make it into the class'
]);
print_r($sample_variable);
The print_r($sample_variable); after this instantiation yields the following:
MyClass Object ( [myVar1:protected] => [myVar2:protected] => 123 )
Because we've initialize $group to null in our __construct(...), it is also valid to pass nothing whatsoever into the constructor as well, like so...
$sample_variable = new MyClass();
print_r($sample_variable);
Now the output is exactly as expected:
MyClass Object ( [myVar1:protected] => [myVar2:protected] => )
The reason I wrote this was so that I could directly pass the output of json_decode(...) to my constructor, and not worry about it too much.
This was executed in PHP 7.1. Enjoy!
I was facing the same issue on creating multiple constructors with different signatures but unfortunately, PHP doesn't offer a direct method to do so. Howerever, I found a trick to overcome that. Hope works for all of you too.
<?PHP
class Animal
{
public function __construct()
{
$arguments = func_get_args();
$numberOfArguments = func_num_args();
if (method_exists($this, $function = '__construct'.$numberOfArguments)) {
call_user_func_array(array($this, $function), $arguments);
}
}
public function __construct1($a1)
{
echo('__construct with 1 param called: '.$a1.PHP_EOL);
}
public function __construct2($a1, $a2)
{
echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
}
public function __construct3($a1, $a2, $a3)
{
echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
}
}
$o = new Animal('sheep');
$o = new Animal('sheep','cat');
$o = new Animal('sheep','cat','dog');
// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog
This is my take on it (build for php 5.6).
It will look at constructor parameter types (array, class name, no description) and compare the given arguments. Constructors must be given with least specificity last. With examples:
// demo class
class X {
public $X;
public function __construct($x) {
$this->X = $x;
}
public function __toString() {
return 'X'.$this->X;
}
}
// demo class
class Y {
public $Y;
public function __construct($y) {
$this->Y = $y;
}
public function __toString() {
return 'Y'.$this->Y;
}
}
// here be magic
abstract class MultipleConstructors {
function __construct() {
$__get_arguments = func_get_args();
$__number_of_arguments = func_num_args();
$__reflect = new ReflectionClass($this);
foreach($__reflect->getMethods() as $__reflectmethod) {
$__method_name = $__reflectmethod->getName();
if (substr($__method_name, 0, strlen('__construct')) === '__construct') {
$__parms = $__reflectmethod->getParameters();
if (count($__parms) == $__number_of_arguments) {
$__argsFit = true;
foreach ($__parms as $__argPos => $__param) {
$__paramClass= $__param->getClass();
$__argVar = func_get_arg($__argPos);
$__argVarType = gettype($__argVar);
$__paramIsArray = $__param->isArray() == true;
$__argVarIsArray = $__argVarType == 'array';
// parameter is array and argument isn't, or the other way around.
if (($__paramIsArray && !$__argVarIsArray) ||
(!$__paramIsArray && $__argVarIsArray)) {
$__argsFit = false;
continue;
}
// class check
if ((!is_null($__paramClass) && $__argVarType != 'object') ||
(is_null($__paramClass) && $__argVarType == 'object')){
$__argsFit = false;
continue;
}
if (!is_null($__paramClass) && $__argVarType == 'object') {
// class type check
$__paramClassName = "N/A";
if ($__paramClass)
$__paramClassName = $__paramClass->getName();
if ($__paramClassName != get_class($__argVar)) {
$__argsFit = false;
}
}
}
if ($__argsFit) {
call_user_func_array(array($this, $__method_name), $__get_arguments);
return;
}
}
}
}
throw new Exception("No matching constructors");
}
}
// how to use multiple constructors
class A extends MultipleConstructors {
public $value;
function __constructB(array $hey) {
$this->value = 'Array#'.count($hey).'<br/>';
}
function __construct1(X $first) {
$this->value = $first .'<br/>';
}
function __construct2(Y $second) {
$this->value = $second .'<br/>';
}
function __constructA($hey) {
$this->value = $hey.'<br/>';
}
function __toString() {
return $this->value;
}
}
$x = new X("foo");
$y = new Y("bar");
$aa = new A(array("one", "two", "three"));
echo $aa;
$ar = new A("baz");
echo $ar;
$ax = new A($x);
echo $ax;
$ay = new A($y);
echo $ay;
Result:
Array#3
baz
Xfoo
Ybar
Instead of the terminating exception if no constructor is found, it could be remove and allow for "empty" constructor. Or whatever you like.
Let me add my grain of sand here
I personally like adding a constructors as static functions that return an instance of the class (the object). The following code is an example:
class Person
{
private $name;
private $email;
public static function withName($name)
{
$person = new Person();
$person->name = $name;
return $person;
}
public static function withEmail($email)
{
$person = new Person();
$person->email = $email;
return $person;
}
}
Note that now you can create instance of the Person class like this:
$person1 = Person::withName('Example');
$person2 = Person::withEmail('yo#mi_email.com');
I took that code from:
http://alfonsojimenez.com/post/30377422731/multiple-constructors-in-php
Hmm, surprised I don't see this answer yet, suppose I'll throw my hat in the ring.
class Action {
const cancelable = 0;
const target = 1
const type = 2;
public $cancelable;
public $target;
public $type;
__construct( $opt = [] ){
$this->cancelable = isset($opt[cancelable]) ? $opt[cancelable] : true;
$this->target = isset($opt[target]) ? $opt[target] : NULL;
$this->type = isset($opt[type]) ? $opt[type] : 'action';
}
}
$myAction = new Action( [
Action::cancelable => false,
Action::type => 'spin',
.
.
.
]);
You can optionally separate the options into their own class, such as extending SplEnum.
abstract class ActionOpt extends SplEnum{
const cancelable = 0;
const target = 1
const type = 2;
}
Starting with PHP 8 we can use named arguments:
class Student {
protected int $id;
protected string $name;
public function __construct(int $id = null, string $name = null, array $row_from_database = null) {
if ($id !== null && $name !== null && $row_from_database === null) {
$this->id = $id;
$this->name = $name;
} elseif ($id === null && $name === null
&& $row_from_database !== null
&& array_keys($row_from_database) === [ 'id', 'name' ]
&& is_int($row_from_database['id'])
&& is_string($row_from_database['name'])) {
$this->id = $row_from_database['id'];
$this->name = $row_from_database['name'];
} else {
throw new InvalidArgumentException('Invalid arguments');
}
}
}
$student1 = new Student(id: 3, name: 'abc');
$student2 = new Student(row_from_database: [ 'id' => 4, 'name' => 'def' ]);
With proper checking it is possible to rule out invalid combinations of arguments, so that the created instance is a valid one at the end of the constructor (but errors will only be detected at runtime).
For php7, I compare parameters type as well, you can have two constructors with same number of parameters but different type.
trait GenericConstructorOverloadTrait
{
/**
* #var array Constructors metadata
*/
private static $constructorsCache;
/**
* Generic constructor
* GenericConstructorOverloadTrait constructor.
*/
public function __construct()
{
$params = func_get_args();
$numParams = func_num_args();
$finish = false;
if(!self::$constructorsCache){
$class = new \ReflectionClass($this);
$constructors = array_filter($class->getMethods(),
function (\ReflectionMethod $method) {
return preg_match("/\_\_construct[0-9]+/",$method->getName());
});
self::$constructorsCache = $constructors;
}
else{
$constructors = self::$constructorsCache;
}
foreach($constructors as $constructor){
$reflectionParams = $constructor->getParameters();
if(count($reflectionParams) != $numParams){
continue;
}
$matched = true;
for($i=0; $i< $numParams; $i++){
if($reflectionParams[$i]->hasType()){
$type = $reflectionParams[$i]->getType()->__toString();
}
if(
!(
!$reflectionParams[$i]->hasType() ||
($reflectionParams[$i]->hasType() &&
is_object($params[$i]) &&
$params[$i] instanceof $type) ||
($reflectionParams[$i]->hasType() &&
$reflectionParams[$i]->getType()->__toString() ==
gettype($params[$i]))
)
) {
$matched = false;
break;
}
}
if($matched){
call_user_func_array(array($this,$constructor->getName()),
$params);
$finish = true;
break;
}
}
unset($constructor);
if(!$finish){
throw new \InvalidArgumentException("Cannot match construct by params");
}
}
}
To use it:
class MultiConstructorClass{
use GenericConstructorOverloadTrait;
private $param1;
private $param2;
private $param3;
public function __construct1($param1, array $param2)
{
$this->param1 = $param1;
$this->param2 = $param2;
}
public function __construct2($param1, array $param2, \DateTime $param3)
{
$this->__construct1($param1, $param2);
$this->param3 = $param3;
}
/**
* #return \DateTime
*/
public function getParam3()
{
return $this->param3;
}
/**
* #return array
*/
public function getParam2()
{
return $this->param2;
}
/**
* #return mixed
*/
public function getParam1()
{
return $this->param1;
}
}
More modern aproach:
You are mixing seperate classes into one, entity & data hydration.
So for your case you should have 2 classes:
class Student
{
protected $id;
protected $name;
// etc.
}
class StudentHydrator
{
public function hydrate(Student $student, array $data){
$student->setId($data['id']);
if(isset($data['name')){
$student->setName($data['name']);
}
// etc. Can be replaced with foreach
return $student;
}
}
//usage
$hydrator = new StudentHydrator();
$student = $hydrator->hydrate(new Student(), ['id'=>4]);
$student2 = $hydrator->hydrate(new Student(), $rowFromDB);
Also please note that you should use doctrine or other ORM that already provides automatic entity hydration.
And you should use dependency injection in order to skip mannualy creating objects like StudentHydrator.
Kris's answer is great, but as Buttle Butku commented, new static() would be preferred in PHP 5.3+.
So I'd do it like this (modified from Kris's answer):
<?php
class Student
{
public function __construct() {
// allocate your stuff
}
public static function withID( $id ) {
$instance = new static();
$instance->loadByID( $id );
return $instance;
}
public static function withRow( array $row ) {
$instance = new static();
$instance->fill( $row );
return $instance;
}
protected function loadByID( $id ) {
// do query
$row = my_awesome_db_access_stuff( $id );
$this->fill( $row );
}
protected function fill( array $row ) {
// fill all properties from array
}
}
?>
Usage:
<?php
$student1 = Student::withID($id);
$student2 = Student::withRow($row);
?>
I also found an useful example in php.net OOP document.
In response to the best answer by Kris (which amazingly helped design my own class btw), here is a modified version for those that might find it useful. Includes methods for selecting from any column and dumping object data from array. Cheers!
public function __construct() {
$this -> id = 0;
//...
}
public static function Exists($id) {
if (!$id) return false;
$id = (int)$id;
if ($id <= 0) return false;
$mysqli = Mysql::Connect();
if (mysqli_num_rows(mysqli_query($mysqli, "SELECT id FROM users WHERE id = " . $id)) == 1) return true;
return false;
}
public static function FromId($id) {
$u = new self();
if (!$u -> FillFromColumn("id", $id)) return false;
return $u;
}
public static function FromColumn($column, $value) {
$u = new self();
if (!$u -> FillFromColumn($column, $value)) return false;
return $u;
}
public static function FromArray($row = array()) {
if (!is_array($row) || $row == array()) return false;
$u = new self();
$u -> FillFromArray($row);
return $u;
}
protected function FillFromColumn($column, $value) {
$mysqli = Mysql::Connect();
//Assuming we're only allowed to specified EXISTENT columns
$result = mysqli_query($mysqli, "SELECT * FROM users WHERE " . $column . " = '" . $value . "'");
$count = mysqli_num_rows($result);
if ($count == 0) return false;
$row = mysqli_fetch_assoc($result);
$this -> FillFromArray($row);
}
protected function FillFromArray(array $row) {
foreach($row as $i => $v) {
if (isset($this -> $i)) {
$this -> $i = $v;
}
}
}
public function ToArray() {
$m = array();
foreach ($this as $i => $v) {
$m[$i] = $v;
}
return $m;
}
public function Dump() {
print_r("<PRE>");
print_r($this -> ToArray());
print_r("</PRE>");
}
Call constructors by data type:
class A
{
function __construct($argument)
{
$type = gettype($argument);
if($type == 'unknown type')
{
// type unknown
}
$this->{'__construct_'.$type}($argument);
}
function __construct_boolean($argument)
{
// do something
}
function __construct_integer($argument)
{
// do something
}
function __construct_double($argument)
{
// do something
}
function __construct_string($argument)
{
// do something
}
function __construct_array($argument)
{
// do something
}
function __construct_object($argument)
{
// do something
}
function __construct_resource($argument)
{
// do something
}
// other functions
}
You could always add an extra parameter to the constructor called something like mode and then perform a switch statement on it...
class myClass
{
var $error ;
function __construct ( $data, $mode )
{
$this->error = false
switch ( $mode )
{
'id' : processId ( $data ) ; break ;
'row' : processRow ( $data ); break ;
default : $this->error = true ; break ;
}
}
function processId ( $data ) { /* code */ }
function processRow ( $data ) { /* code */ }
}
$a = new myClass ( $data, 'id' ) ;
$b = new myClass ( $data, 'row' ) ;
$c = new myClass ( $data, 'something' ) ;
if ( $a->error )
exit ( 'invalid mode' ) ;
if ( $b->error )
exit ('invalid mode' ) ;
if ( $c->error )
exit ('invalid mode' ) ;
Also with that method at any time if you wanted to add more functionality you can just add another case to the switch statement, and you can also check to make sure someone has sent the right thing through - in the above example all the data is ok except for C as that is set to "something" and so the error flag in the class is set and control is returned back to the main program for it to decide what to do next (in the example I just told it to exit with an error message "invalid mode" - but alternatively you could loop it back round until valid data is found).
I created this method to let use it not only on constructors but in methods:
My constructor:
function __construct() {
$paramsNumber=func_num_args();
if($paramsNumber==0){
//do something
}else{
$this->overload('__construct',func_get_args());
}
}
My doSomething method:
public function doSomething() {
$paramsNumber=func_num_args();
if($paramsNumber==0){
//do something
}else{
$this->overload('doSomething',func_get_args());
}
}
Both works with this simple method:
public function overloadMethod($methodName,$params){
$paramsNumber=sizeof($params);
//methodName1(), methodName2()...
$methodNameNumber =$methodName.$paramsNumber;
if (method_exists($this,$methodNameNumber)) {
call_user_func_array(array($this,$methodNameNumber),$params);
}
}
So you can declare
__construct1($arg1), __construct2($arg1,$arg2)...
or
methodName1($arg1), methodName2($arg1,$arg2)...
and so on :)
And when using:
$myObject = new MyClass($arg1, $arg2,..., $argN);
it will call __constructN, where you defined N args
then
$myObject -> doSomething($arg1, $arg2,..., $argM)
it will call doSomethingM, , where you defined M args;
Related
Uncertain number objects owned by another object
I have a Project class/object that needs to have (own) an uncertain number of Phase objects. I don't know the number of phases the project object will have when it is created, so I didn't want to put Phase object creation in the constructor function of Project. My classes: class Project { //some properties } class Phase { public $property; } And I'd like to do this: $foo = $myProject->phase01->property; $bar = $myProject->phase06->property; //etc...
I wouldn't use dynamic properties. If the phases are a collection, would treat them as such, it could come handy later on. E.g.: class Project { private $phases = []; public function __get($property) { // if begins with "phase" and some number if ( preg_match("/^phase(\d+)$/", $property, $matches) ) { // if is set already, we return it if ( isset($this->phases[$matches[1]]) ) { return $this->phases[$matches[1]]; } // if it isn't, it isn't :) return null; } } public function __set($property, $value) { if ( preg_match("/^phase(\d+)$/", $property, $matches) ) { $this->phases[$matches[1]] = $value; } } public function addPhase(Phase $phase, $phase_number = null) { if ($phase_number !== null) { $this->phases[$phase_number] = $phase; } else { $this->phases[] = $phase; } return $this; } public function getPhases() { return $this->phases; } // etc } class Phase { public $property = ""; public function __construct($property) { $this->property = $property; } } $myProject = new Project(); $myProject->phase1 = new Phase('startup'); $myProject ->addPhase(new Phase('build')) ->addPhase(new Phase('cleanup')); foreach ($myProject->getPhases() as $key => $phase) { echo "Phase $key: {$phase->property}", "\n"; }
You could implement one of php's magic methods, in particular __get <?php class Project { //some properties public function __get($property) { // if begins with "phase" and some number if ( preg_match("/^phase\d+$/", $property) === 1 ) { if ( !isset($this->$property) ) { $this->$property = new Phase; } return $this->$property; } } } class Phase { public $property; } $myProject = new Project; //And I'd like to do this: $foo = $myProject->phase01->property; $bar = $myProject->phase06->property; //etc...
PHP mandatory function call
I understand that one can use interfaces to mandate the definition of a function, but I cannot find something that enables one to mandate function calls, such that e.g. if I create a class being a member of another class (via extends, etc), with a function, for that class to automatically ensure that mandatory functions are called in part with that function. I mean, to clarify further: class domain { function isEmpty($input) { //apply conditional logic and results } } class test extends domain { function addTestToDBTable($test) { /** * try to add but this class automatically makes it so that all rules of * class domain must be passed before it can run * - so essentially, I am no longer required to call those tests for each and * every method **/ } } Apologies if this appears incoherent by any means. Sure, it seems lazy but I want to be able to force context without having to concern abou Update: Okay, to clarify further: in PHP, if I extend and declare a __construct() for a child class, that child class will override the parent __construct(). I do not want this, I want the parent construct to remain and mandate whatever as it pleases just as the child class may do so also.
I guess it can be done in two different ways. Aspect Oriented Programming Have a look here https://github.com/AOP-PHP/AOP Generate or write Proxy classes A really simple example could be: <?php class A { public function callMe() { echo __METHOD__ . "\n"; } } class B extends A { // prevents instantiation public function __construct() { } public function shouldCallMe() { echo __METHOD__ . "\n"; } public static function newInstance() { return new ABProxy(); } } class ABProxy { private $b; public function __construct() { $this->b = new B(); } public function __call($method, $args) { $this->b->callMe(); return call_user_func_array(array($this->b, $method), $args); } } // make the call $b = B::newInstance(); $b->shouldCallMe(); // Outputs // ------------------ // A::callMe // B::shouldCallMe Hopes this helps a bit.
Sounds like you want a Decorator. See This answer for a detailed explanation on how to do it. Note that it does not require a class extension.
I would use a domain-validating decorator with some doc-block metaprogramming magic. But this is really a job for an entire library, which no doubt exists. fiddle <?php class FooDomain { public static function is_not_empty($input) { return !empty($input); } } class Foo { /** * #domain FooDomain::is_not_empty my_string */ public function print_string($my_string) { echo $my_string . PHP_EOL; } } $foo = new DomainValidator(new Foo()); $foo->print_string('Hello, world!'); try { $foo->print_string(''); // throws a DomainException } catch (\DomainException $e) { echo 'Could not print an empty string...' . PHP_EOL; } // --- class DomainValidator { const DOMAIN_TAG = '#domain'; private $object; public function __construct($object) { $this->object = $object; } public function __call($function, $arguments) { if (!$this->verify_domain($function, $arguments)) { throw new \DomainException('Bad domain!'); } return call_user_func_array( array($this->object, $function), $arguments ); } public function __get($name) { return $this->object->name; } public function __set($name, $value) { $this->object->name = $value; } private function verify_domain($function, $arguments) { // Get reference to method $method = new \ReflectionMethod($this->object, $function); $domains = $this->get_domains($method->getDocComment()); $arguments = $this->parse_arguments( $method->getParameters(), $arguments ); foreach ($domains as $domain) { if (!call_user_func( $domain['name'], $arguments[$domain['parameter']] )) { return false; } } return true; } private function get_domains($doc_block) { $lines = explode("\n", $doc_block); $domains = array(); $domain_tag = DomainValidator::DOMAIN_TAG . ' '; foreach ($lines as $line) { $has_domain = stristr($line, $domain_tag) !== false; if ($has_domain) { $domain_info = explode($domain_tag, $line); $domain_info = explode(' ', $domain_info[1]); $domains[] = array( 'name' => $domain_info[0], 'parameter' => $domain_info[1], ); } } return $domains; } private function parse_arguments($parameters, $values) { $ret = array(); for ($i = 0, $size = sizeof($values); $i < $size; $i++) { $ret[$parameters[$i]->name] = $values[$i]; } return $ret; } } Output: Hello, world! Could not print an empty string...
PHP 5 how to call multiple values from one function?
If I have the following class example: <?php class Person { private $prefix; private $givenName; private $familyName; private $suffix; public function setPrefix($prefix) { $this->prefix = $prefix; } public function getPrefix() { return $this->prefix; } public function setGivenName($gn) { $this->givenName = $gn; } public function getGivenName() { return $this->givenName; } public function setFamilyName($fn) { $this->familyName = $fn; } public function getFamilyName() { return $this->familyName; } public function setSuffix($suffix) { $this->suffix = $suffix; } public function getSuffix() { return $suffix; } } $person = new Person(); $person->setPrefix("Mr."); $person->setGivenName("John"); echo($person->getPrefix()); echo($person->getGivenName()); ?> I there a way in PHP (5.4 preferably), to combine these return values into one function, this way it models a little bit more like the revealing module pattern in JavaScript? UPDATE: OK, I am now beginning to learn that within PHP, it is normative to return a single value from a function, but you "can" return an array of multiple values. This is the ultimate answer to my question and what I will dive into some practices with this understanding. small example - function fruit () { return [ 'a' => 'apple', 'b' => 'banana' ]; } echo fruit()['b']; Also an article I ran across on stackoverflow on the topic... PHP: Is it possible to return multiple values from a function? Good luck!
You sound like you want the __get() magic method. class Thing { private $property; public function __get($name) { if( isset( $this->$name ) { return $this->$name; } else { throw new Exception('Cannot __get() class property: ' . $name); } } } // -- end class Thing -- $athing = new Thing(); $prop = $athing->property; In the case that you want all of the values returned at once, as in Marc B's example, I'd simplify the class design for it thusly: class Thing { private $properties = array(); public function getAll() { return $properties; } public function __get($name) { if( isset( $this->properties[$name] ) { return $this->properties[$name]; } else { throw new Exception('Cannot __get() class property: ' . $name); } } } // -- end class Thing -- $athing = new Thing(); $prop = $athing->property; $props = $athing-> getAll();
Perhaps public function getAll() { return(array('prefix' => $this->prefix, 'givenName' => $this->giveName, etc...)); }
get set properties in php
I'm from the C# environment and I'm starting to learn PHP in school. I'm used to set my properties in C# like this. public int ID { get; set; } What's the equivalent to this in php? Thanks.
There is none, although there are some proposals for implementing that in future versions. For now you unfortunately need to declare all getters and setters by hand. private $ID; public function setID($ID) { $this->ID = $ID; } public function getID() { return $this->ID; } for some magic (PHP likes magic), you can look up __set and __get magic methods. Example class MyClass { private $ID; private function setID($ID) { $this->ID = $ID; } private function getID() { return $this->ID; } public function __set($name,$value) { switch($name) { //this is kind of silly example, bt shows the idea case 'ID': return $this->setID($value); } } public function __get($name) { switch($name) { case 'ID': return $this->getID(); } } } $object = new MyClass(); $object->ID = 'foo'; //setID('foo') will be called
Thanks for your answers everyone. It helped me to create something like this: In my parent class: public function __get($name){ if (ObjectHelper::existsMethod($this,$name)){ return $this->$name(); } return null; } public function __set($name, $value){ if (ObjectHelper::existsMethod($this,$name)) $this->$name($value); } ObjectHelper::existsMethod is a method which just check if given protected method exists. private $_propertyName = null; protected function PropertyName($value = ""){ if (empty($value)) // getter { if ($this-> _propertyName != null) return $this->_propertyName; } else // setter { $this-> _propertyName = $value; } return null; } So I can use something like this in any class: $class = new Class(); $class->PropertyName = "test"; echo $class->PropertyName; I was inspired by C# :) What do you think about this, guys? Here is my ObjectHelper if someone would like to use it: namespace Helpers; use ReflectionMethod; class ObjectHelper { public static function existsMethod($obj, $methodName){ $methods = self::getMethods($obj); $neededObject = array_filter( $methods, function ($e) use($methodName) { return $e->Name == $methodName; } ); if (is_array($neededObject)) return true; return false; } public static function getMethods($obj){ $var = new \ReflectionClass($obj); return $var->getMethods(ReflectionMethod::IS_PROTECTED); } }
Mchi is right, but there is another way of doing it by using single function private $ID; public function ID( $value = "" ) { if( empty( $value ) ) return $this->ID; else $this->ID = $value; } But yeah this approach is pretty much inline with what you do in c#. but this is only an alternative Or try using php's __set and __get in your class more info here http://php.net/manual/en/language.oop5.overloading.php
Another exampled using Variable function name class MyClass { private $ID; protected $ID2; private function setID($ID) { $this->ID = $ID; } private function getID() { return $this->ID; } private function setID2($ID2) { $this->ID2 = $ID2; } private function getID2() { return $this->ID2; } public function __set($name,$value) { $functionname='set'.$name; return $this->$functionname($value); } public function __get($name) { $functionname='get'.$name; return $this->$functionname(); } } $object = new MyClass(); $object->ID = 'foo'; //setID('foo') will be called $object->ID2 = 'bar'; //setID2('bar') will be called
private $ID; public function getsetID($value = NULL) { if ($value === NULL) { return $this->ID; } else { $this->ID = $value; } }
I know I am a bit late to the party on this question, but I had the same question/thought myself. As a C# developer who does PHP, when the job requires, I want to have a simple way to create properties just I would be able to in C#. I whipped up a first draft this afternoon which allows you to create the backing fields and specify their accessors or have pure accessors with no backing field. I will update my answer as the code evolves and provide a link when I get it to the state where it can be imported as a composer package. For simplicity, I created the functionality as a PHP trait so you can drop it in to any class you want instead of having to extend a base class. Eventually I hope to extend this functionality to discern between external public calls to the properties and protected/private calls. Here is the code for the trait itself: trait PropertyAccessorTrait { private static $__propertyAccessors = []; /* #property string $__propertyPrefix */ public function __get($name) { $this->__populatePropertyAcessors($name); return $this->__performGet($name); } public function __set($name, $value) { $this->__populatePropertyAcessors($name); $this->__performSet($name, $value); } public function __isset($name) { // TODO: Implement __isset() method. } public function __unset($name) { // TODO: Implement __unset() method. } protected function __getBackingFieldName($name) { if (property_exists(self::class, '__propertyPrefix')) { $prefix = $this->__propertyPrefix; } else { $prefix = ''; } return $prefix . $name; } protected function __canget($name) { $accessors = $this->__getPropertyAccessors($name); return $accessors !== null && isset($accessors['get']); } protected function __canset($name) { $accessors = $this->__getPropertyAccessors($name); return $accessors !== null && isset($accessors['set']); } protected function __performGet($name) { if (!$this->__canget($name)) { throw new \Exception('Getter not allowed for property: ' . $name); } $accessors = $this->__getPropertyAccessors($name)['get']; /* #var \ReflectionMethod $method */ $method = $accessors['method']; if (!empty($method)) { return $method->invoke($this); } return $this->{$this->__getBackingFieldName($name)}; } protected function __performSet($name, $value) { if (!$this->__canset($name)) { throw new \Exception('Setter not allowed for property: ' . $name); } $accessors = $this->__getPropertyAccessors($name)['set']; /* #var \ReflectionMethod $method */ $method = $accessors['method']; if (!empty($method)) { return $method->invoke($this, $value); } $this->{$this->__getBackingFieldName($name)} = $value; } protected function __getPropertyAccessors($name) { return isset(self::$__propertyAccessors[$name]) ? self::$__propertyAccessors[$name] : null ; } protected function __getAccessorsFromDocBlock($docblock) { $accessors = []; if (!empty(trim($docblock))) { $doclines = null; if (!empty($docblock)) { $doclines = explode("\n", $docblock); } if (!empty($doclines)) { foreach ($doclines as $line) { if (preg_match('/#(get|set)\\s+(public|private|protected)/', $line, $matches)) { $accessors[$matches[1]]['visibility'] = $matches[2]; } } } } return $accessors; } protected function __populatePropertyAcessors($name) { if ($this->__getPropertyAccessors($name) !== null) return; try { $property = new \ReflectionProperty(self::class, $this->__getBackingFieldName($name)); } catch (\ReflectionException $ex) { $property = null; } $accessors = []; if ($property != null) { $accessors = $this->__getAccessorsFromDocBlock($property->getDocComment()); } try { $methodName = 'get' . ucfirst($name); $method = new \ReflectionMethod(self::class, $methodName); $method->setAccessible(true); $accessors = array_merge($accessors, $this->__getAccessorsFromDocBlock($method->getDocComment())); } catch (\ReflectionException $ex) { $method = null; } if ($method !== null || isset($accessors['get'])) { $accessors['get']['method'] = $method; } try { $methodName = 'set' . ucfirst($name); $method = new \ReflectionMethod(self::class, $methodName); $method->setAccessible(true); $accessors = array_merge($accessors, $this->__getAccessorsFromDocBlock($method->getDocComment())); } catch (\ReflectionException $ex) { $method = null; } if ($method !== null || isset($accessors['set'])) { $accessors['set']['method'] = $method; } self::$__propertyAccessors[$name] = $accessors; } } Here is a quick unit test I created using the Codeception format: <?php class PropertyAssesorTraitTestClass { use PropertyAccessorTrait; private $__propertyPrefix = '_'; /** * #get public * #set public */ private $_integer = 1; /** * #get public */ private $_getonly = 100; /** * #set public */ private $_setonly; private $_customDoubler; private function getCustomDoubler() { return $this->_customDoubler * 2; } private function setCustomDoubler($value) { $this->_customDoubler = $value * 2; } public $publicField = 1234; /** * #return int * #get public */ private function getPureAccessor() { return $this->publicField; } /** * #param $value * #set public */ private function setPureAccessor($value) { $this->publicField = $value; } private $_purePrivate = 256; } $I = new UnitTester($scenario); $I->wantTo('Ensure properties are accessed correctly'); $instance = new PropertyAssesorTraitTestClass(); $I->assertSame(1, $instance->integer); $instance->integer = 2; $I->assertSame(2, $instance->integer); $instance->integer = $instance->integer + 1; $I->assertSame(3, $instance->integer); $instance->integer++; $I->assertSame(4, $instance->integer); $I->assertSame(100, $instance->getonly); $I->expectException('Exception', function () use ($instance) { $instance->getonly = 50; }); $instance->setonly = 50; $I->expectException('Exception', function () use ($instance) { $a = $instance->setonly; }); $instance->customDoubler = 100; $I->assertSame(400, $instance->customDoubler); $I->assertSame(1234, $instance->publicField); $instance->pureAccessor = 1000; $I->assertSame(1000, $instance->publicField); $instance->publicField = 1234; $I->assertSame(1234, $instance->publicField); $I->assertSame(1234, $instance->pureAccessor); $I->expectException('Exception', function () use ($instance) { return $instance->purePrivate; });
I like to use this pattern: class foo { //just add p as prefix to be different than method name. protected $pData; public funtion __construct() {} public funtion __destruct() {} public funtion __clone() {} public function Data($value == "") { if ($value != "") { $this->pData = $value; } return $this->pData; } } $myVar = new foo(); //for SET $myVar->Data("A Value"); //for GET $item = $myVar->Data();
class MyClass { private $name = null; public function __construct($name = null) { $this->name = $name; } public function __set($name, $value) { if (property_exists($this, $name)) { $this->name = $value; } return $this; } public function __get($name) { if (property_exists($this, $name)) { return $this->$name; } return null; } }
this is PHP ; you don't need get set class MyClass { public $ID; } $object = new MyClass(); $object->ID = 'foo'; echo $object->ID; will work
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' ]