I see a lot of code where the calls are like this.
An example:
$person->head->eyes->color = "brown";
$person->head->size = 10;
$person->name = "Daniel";
How do I achieve what I wrote above?
That just means that $person, $person->head, and $person->eyes each have properties that are other objects. head is a property of $person, eyes is a property of $person->head, and so on.
So, when you set $person->head->size, for example, you are setting the size property of $person->head, meaning $person->head must be an object. Put differently, the statement $person->head->size = 10; means set the size property of the head property of $person to 10.
Example Code:
<?php
class Eyes
{
var $color = null;
}
class Head
{
var $eyes = null;
var $size = null;
function __construct()
{
$this->eyes = new Eyes();
}
}
class Person
{
var $head = null;
var $name = null;
function __construct()
{
$this->head = new Head();
}
}
$person = new Person();
$person->head->eyes->color = "brown";
$person->head->size = 10;
$person->name = "Daniel";
var_dump($person);
This outputs:
class Person#1 (2) {
public $head =>
class Head#2 (2) {
public $eyes =>
class Eyes#3 (1) {
public $color =>
string(5) "brown"
}
public $size =>
int(10)
}
public $name =>
string(6) "Daniel"
}
First thing: there are no method being called in your example.
To the answer:
This can be achieved by using another objects instances as attributes. Eg:
class Head{
public $size, $eyes, $etc;
}
class Person{
public $name, $age, $head;
public function __construct(){
$this->head = new Head();
}
}
$person = new Person();
$person->head->size = 'XL';
This is one way of doing it
You can also cast arrays as objects. This will generate stdClass instances with array indexes as attributes:
$person = array(
'name' => 'Foo',
'age' => 20
);
$personObject = (object) $person;
var_dump($personObject);
PHP method chaning is the secret, return on each getter method $this
class Person
{
public function head()
{
...
return $this;
}
public function eyes()
{
...
return $this;
}
}
$person->head->eyes->color = "brown";
https://en.wikipedia.org/wiki/Method_chaining#PHP
Related
Assume I have an object with 3 properties:
protected $validMainStatements;
protected $validPrimaryStatements;
protected $validSecondaryStatements;
And I got the following method:
public function selectType($stmt) {
$stmtParts = MainServerInterface::parse($stmt);
$type = $stmtParts[0] //returns either Main, Primary or Secondary
}
Depending on the value of type, I want to use the associated property. A simple implementation would be:
public function selectType($stmt) {
$stmtParts = MainServerInterface::parse($stmt);
$type = $stmtParts[0] //returns either Main, Primary or Secondary
if($type === "Main") {
$usedProp = $this->validMainStatements;
} elseif($type === "Primary") {
$usedProp = $this->validPrimaryStatements;
} elseif($type === "Secondary") {
$usedProp = $this->validSecondaryStatements;
}
}
I think I don't have to mention that this is ugly and uncomfortable to use. Is there a way to implement this in an easier way? Something like (pseudocode):
$usedProp = $"valid".$type."Statements";
<?php
class Foo {
protected $validMainStatements = 1;
protected $validPrimaryStatements = 2;
protected $validSecondaryStatements = 3;
public function bar() {
$type = 'Primary';
return $this->{'valid'.$type.'Statements'};
}
}
$foo = new Foo;
echo $foo->bar();
see Variable variables - Example #1 Variable property example
-- edit and btw:
I'd rather do it this way:
<?php
class Foo {
protected $validStatements = [
'Main' => 1,
'Primary' => 2,
'Secondary' => 3
];
public function bar() {
$type = 'Primary';
return $this->validStatements[$type];
}
}
$foo = new Foo;
echo $foo->bar();
Demo
Try like below
$usedProp = $this->{"valid".$type."Statements"};
Test
<?php
class test {
public $validMainStatements = 'hello';
}
$instance = new test;
$type = 'Main';
echo $instance->{"valid".$type."Statements"};
?>
Result
hello
Just use variable variables.
$variableName = 'valid'.$type.'Statements';
$this->{$variableName};
Let's say I have this:
class "classname"
{
....
public function section($id)
{
// variable method name
$this->section->$id = new stdClass();
return $this;
}
public function subsection()
{
// $id is not available here
$this->section->$id->subsection = array();
return $this;
}
....
}
When I call:
$classname->section("test")
->subsection();
It is not working because $id is not global nor set in the second chainlink. Do I have to pass it manually to ->subsection($id) or is there a more generic/cleaner way to get it there?
What I try to accomplish here is to create an (big) object with multiple sections. In these sections objects and/or array's so there are more (chained) methods involved.
You can act like this way:
class Foo
{
protected $section;
private $lastUsedId = null;
public function __construct()
{
$this->section = new StdClass();
}
public function section($id)
{
// variable method name
$this->section->$id = new StdClass();
$this->lastUsedId = $id;
return $this;
}
public function subsection()
{
// $id is not available here
$this->section->{$this->lastUsedId}->subsection = array();
return $this;
}
}
so
$obj = (new Foo())
->section('one')
->subsection()
->section('two')
->subsection();
will produce valid result like
object(Foo)#1 (2) {
["section":protected]=>
object(stdClass)#2 (2) {
["one"]=>
object(stdClass)#3 (1) {
["subsection"]=>
array(0) {
}
}
["two"]=>
object(stdClass)#4 (1) {
["subsection"]=>
array(0) {
}
}
}
["lastUsedId":"Foo":private]=>
string(3) "two"
}
Note, that it isn't a good idea to use chaining like this way - it's difficult to read, and, besides, having method that actually changes data, but looks like getter, is confusing.
The problem you're facing is not because of chaining methods. It occurs either because you haven't declared the property $section or if you've declared it it has no property $id.
One possibility would be to define $section on the fly in the same way you're doing it with $id, i.e.
public function section($id) {
$this->section = new stdClass();
$this->section->id = new stdClass();
return $this;
}
or
class Classname {
private $section;
public function __construct() {
$this->section = new stdClass();
}
public function section($id) {
$this->section->id = new stdClass();
return $this;
}
}
or
class Classname {
private $section;
public function __construct() {
$this->section = new B();
}
public function section($id) {
$this->section->id = new stdClass();
return $this;
}
}
class B {
private $id;
}
Consider using 2 classes to accomplish what you want. Here is an example
class Document
{
private $sections = array();
public function addSection(Section $section)
{
$this->sections[] = $section;
}
public function getSections()
{
print_r($this->sections);
}
}
class Section {
private $id;
private $subsection;
public function setId($id)
{
$this->id = $id;
return $this;
}
public function setSubsection()
{
$this->subsection = array();
return $this;
}
}
$section1 = new Section;
$section1->setId(1)->setSubsection();
$section2 = new Section;
$section2->setId(2)->setSubsection();
$document = new Document;
$document->addSection($section1);
$document->addSection($section2);
$document->getSections();
will output
Array (
[0] => Section Object ([id:protected] => 1 [subsection:protected] => Array( ))
[1] => Section Object ([id:protected] => 2 [subsection:protected] => Array( )))
I have three php classes. I can instantiate them this way:
$piza = new Mashrooms(new SeaFood(new PlainPiza()));
However when I try to instantiate them dynamically in this way:
$temp = Mashrooms(new SeaFood(new PlainPiza()));
$piza = new $temp;
it fails and shows this error:
Fatal error: Class 'SeaFood(new Mashrooms' not found.
Your help will be appreciated.
$temp is an Object, not a Class, and you cannot use the new keyword on an existing Object.
$plain = 'PlainPiza';
$seafood = 'SeaFood';
$mashrooms = 'Mashrooms';
$piza = new $mashrooms(new $seafood(new $plain)));
Given the new information
The problem is I don't know how many classes I will instantiate
I think your approach my be wrong. Have you thought about having a Pizza class and adding your topping objects to the pizza object? For instance:
<?php
class Pizza
{
private $_toppings;
private $_placements = array('left', 'right', 'whole');
public function _construct()
{
foreach($this->_placements as $placement)
{
$this->_toppings[$placement] = array();
}
}
public function add_topping(Base_Topping $topping, $placement)
{
if(in_array($placement, $this->_placements))
{
array_push($this->_toppings[$placement], $topping);
}
}
}
abstract class Base_Topping
{
protected $_price = 0.00;
protected $_name = 'No Name';
public function get_name()
{
return $this->_name;
}
public function get_price()
{
return $this->_price;
}
}
class Mushrooms extends Base_Topping
{
protected $_price = '1.00';
protected $_name = 'Mushrooms';
}
// assuming $_POST['toppings'] = array('Mushrooms' => 'whole', 'Pepperoni' => 0, 'Sausage' => 0, etc...)
$pizza = new Pizza();
$toppings = array_filter($_POST); // will return anything with a non-false value
foreach($toppings as $name => $coverage)
{
$topping = new $name();
$pizza->add_topping($topping, $coverage);
}
?>
This will work:
$temp = "Mashrooms";
$pizza = new $temp(new SeaFood(new PlainPiza()));
See here: http://3v4l.org/RXDLX
I have a PHP Class with a Constructor and some Methods. Every Method need to have the same return stdClass Object. Only a few properties in each functoin of the stdClass Object should be diffrent from the default one(like the property value or status). How would you do that? I mean, i can define in every function an stdClass Object with all properties, but as I said, I only need to change a few properties in each function for the return.
Examplecode which doesn't work:
<?
class Person{
public $sName;
public $oReturn = new stdClass();
$oReturn->status = 200;
$oReturn->value = "Personname";
function __construct($sName) {
$this->sName = $sName;
}
public function something($oData){
//Declaration
$this->oReturn->value = $oData->newName;
//Main
//Return
return $this->oReturn;
}
}
?>
You can't declare properties like this:-
public $oReturn = new stdClass();
That is illegal in PHP. Do it like this:-
class Person{
public $sName;
public $oReturn;
function __construct($sName) {
$this->sName = $sName;
$this->oReturn = new stdClass;
$this->oReturn->status = 200;
$this->oReturn->value = "Personname";
}
public function something($oData){
//Declaration
$this->oReturn->value = $oData->newName;
//Main
//Return
return $this->oReturn;
}
}
Now you can set whatever properties you want in $this->oReturn which, I think, is what you want to achieve.
See it working
You could just return a shared method that formats the data for you.
<?php
class Foo
{
protected
$defaults = array(
'a' => 1,
'b' => 2,
);
protected function reply(array $params = null) {
$properties = $this->defaults;
if ($params) {
$properties = array_merge($properties, $params);
}
return (object) $properties;
}
public function a($a) {
return $this->reply(array('a' => $a));
}
public function b($b) {
return $this->reply(array('b' => $b));
}
}
$foo = new Foo;
var_dump($foo->a('a'));
var_dump($foo->b('b'));
/*
object(stdClass)#2 (2) {
["a"]=>
string(1) "a"
["b"]=>
int(2)
}
object(stdClass)#2 (2) {
["a"]=>
int(1)
["b"]=>
string(1) "b"
}
*/
I think you might be after method chaining. You can create a class whose methods return $this, and then you can change your method calls.
Consider a class like the following:
<?php
class User
{
protected $id;
protected $name;
protected $email;
public function setName($name)
{
$this->name = $name;
return $this;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
}
You can then use it as follows:
<?php
$user = new User();
$user->setName('Martin')->setEmail('martin#example.com');
After this, your User class’s properties will reflect what values you’ve assigned to them in your chained method calls.
You can assign non-scalar values to class properties after class instantiation(AKA after new). See that the $oReturn value assignment is moved into the constructor.
class Person{
public $sName;
public $oReturn;
protected $default_status = 200;
protected $default_value = "Personname";
function __construct($sName) {
$this->sName = $sName;
$this->oReturn = new stdClass();
$this->oReturn->status = $this->default_status;
$this->oReturn->value = $this->default_value;
}
public function something($oData){
//Declaration
$this->oReturn->value = $oData->newName;
//Main
//Return
return $this->oReturn;
}
}
Now, you can extend this class, to make small variations.
class PersonNotFound extends Person {
protected $default_status = 404;
protected $default_value = 'NotFound';
}
Let's see their results:
$oData = new stdClass();
$oData->newName = 'Neo';
$person_a = new Person("Triniti");
var_dump( $person_a->something($oData) );
// status = 200
$person_b => new PersonNotFound("Cyon");
var_dump( $person_b->something($oData) );
// status = 404
EDIT:
Constructor injection version:
class Person{
public $sName;
public $oReturn;
function __construct($sName, $status = 200, $value = "Personname") {
$this->sName = $sName;
$this->oReturn = new stdClass();
$this->oReturn->status = $status;
$this->oReturn->value = $value;
}
public function something($oData){
$this->oReturn->value = $oData->newName;
return $this->oReturn;
}
}
$person_a = new Person("Neo"); // 200, Personname as default
$person_b = new Person("Triniti", 404, "NotFound");
Can we dynamically create and initialize an object in PHP?
This is the normal code:
class MyClass{
var $var1 = null;
var $var2 = null;
.
.
public function __construct($args){
foreach($args as $key => $value)
$this->$key = $value;
}
}
---------------------
$args = ($_SERVER['REQUEST_METHOD'] == "POST") ? $_POST : $_REQUEST;
$obj = new MyClass($args);
The above code works fine. Please note that the names of REQUEST parameters are accurately mapped with the members of class MyClass.
But can we do something like this:
$class = "MyClass";
$obj = new $class;
If we can do like this, then can we initialize $obj by using $args.
According to this post, $obj = $class should work. But it does not work for me. I tried get_class_vars($obj). It threw an exception.
Thanks
It's more a comment, but I leave it here more prominently:
$class = "MyClass";
$obj = new $class($args);
This does work. See newDocs.
You have to overload some other magic methods:
__get (a method that gets called when you call object member)
__set (a method that gets called when you want to set object member)
__isset
__unset
Please see this codepad to see your code rewritten to work with what you want:
<?php
class MyClass{
var $properties = array();
public function __construct($args){
$this->properties = $args;
}
public function __get($name) {
echo "Getting '$name'\n";
if (array_key_exists($name, $this->properties)) {
return $this->properties[$name];
}
return null;
}
}
$args = array("key1" => "value1", "key2" => "value2");
$class = "MyClass";
$obj = new $class($args);
echo "key1:". $obj->key1;
?>
You can use Reflection to instanciate an object with parameters.
<?php
class Foo {
protected $_foo;
protected $_bar;
public function __construct($foo, $bar)
{
$this->_foo = $foo;
$this->_bar = $bar;
}
public function run()
{
echo $this->_foo . ' ' . $this->_bar . PHP_EOL;
}
}
$objectClass = 'Foo';
$args = array('Hello', 'World');
$objectReflection = new ReflectionClass($objectClass);
$object = $objectReflection->newInstanceArgs($args);
$object->run();
See Reflection on php manual.