How this stuff works?
$object->foo1()->foo2()->foo3();
I'm working in Magento php framework. In Magento functions are called as sequence.But, I don't understand how it works.Can anyone explain.
$object->foo1()->foo2()->foo3();
First, PHP will get a pointer to the foo1 method from the $object variable. It calls this method, which then returns another object. This second object (we shall call it $object2) has a method foo2(), which is also called. foo2() returns another object ($object3), which has a method foo3(), which returns whatever it likes.
In some cases, $object, $object2 and $object3 are just pointers to the same object. This means that you can keep calling methods on the same class in a nice sequence.
You can achieve this quite easily:
class Foo
{
public function nicely()
{
return $this;
}
public function formatted()
{
return $this;
}
public function chained()
{
return $this;
}
public function calls()
{
return $this;
}
}
$foo = new Foo();
$foo->nicely()->formatted()->chained()->calls();
When the methods in the class are returning values, they are returning objects of themselves. This allows you to keep calling other methods in that class in a sequence like you posted.
class myClassA {
public $str = NULL;
public function setStr( $value ){
$this->str .= $value;
return $this; // This is the key to sequencing
}
}
$myclass = new MyClassA();
$myclass->setStr("H")->setStr("E");
// If you echoed the $str variable in myClassA, you would get
// "HE"
Related
Say I have the following class:
class Test
{
private static $instance = false;
public static function test()
{
if(!self::$instance)
{
self::$instance = new self();
}
return self::$instance;
}
public function test1()
{
//...
}
public function test2()
{
//...
}
}
And I go about calling functions by chaining them like so:
$data = Test::test(...)->test1(...)->test2(...);
At the moment for the above method chain to work I have to keep returning $instance and I really would like it if I could return something from test2() to then be assigned to $data but I am not sure how to do this as I have to keep returning $instance in order for mt method chain to work?
If you want to chain methods, you need to return the current instance from any method which has another call chained on after it. However, it's not necessary for the last call in the chain to do so. In this case that means you're free to return whatever you like from test2()
Just bear in mind, if you return something different from test2() you'll never be able to chain anything onto it in future. For example, $data = Test::test(...)->test2(...)->test1(...); wouldn't work.
Protip: it's worth documenting your code with some comments explaining which ones are and aren't chainable so you don't forget in future.
Generally speaking if you are doing method chaining, and I'm assuming each of the tests above return your data model in a different state, and I assume that you want some data from the model itself. I would do the following:
class Test
{
private static $model;
public function test1() {
//do something to model
return $this;
}
public function test1() {
//do something to model
return $this;
}
public function finish_process() {
//process results
return $this.model;
}
}
so essentially i can do the following now:
$results = Test::test1()->finish_process();
and
$results = Test::test1()->test2()->finish_process();
You can pass the $data by its reference and you can change it or assign any data into it.
// inside class
public function test2( &$data ) {
$data = 'it will work';
}
// outside class
$data = '';
Test::test(...)->test1(...)->test2($data);
check this http://php.net/manual/en/language.references.pass.php
return $this inside test1() and test2() methods.
I am not quite sure why this is happening, or how to properly explain it, but maybe someone can shed some light on this.
I have a CMS system I based off of the CodeIgniter/Opencart Framework utilizing a Registry, Controller and Module. I have run into a scenario where I have previously saved a variable to the registry as:
$this->application_page = 'current/page';
But for some reason when I call it in the application:
echo empty($this->application_page)?'yes':'no';
//Returns Yes
But.. When I Reassign it:
echo empty($this->application_page)?'yes':'no';
//Returns Yes
$page = $this->application_page;
echo empty($page)?'yes':'no';
//Returns No
A var_dump returns:
var_dump($this->application_page);
string 'current/page' (length=12)
I can get around this quite easily by just using $page but I'm curious to know why this is happening?
UPDATE:
So I messed around with the _isset function but didn't get it to work, possibly my error, possibly not.. Here is how it all works together:
class Registry {
private $data = array();
public function get($key){ return (isset($this->data[$key]) ? $this->data[$key] : NULL); }
public function set($key,$val){ $this->data[$key] = $val;
}
abstract class Controller {
protected $registry;
public function __construct($registry){ $this->registry = $registry; }
public function __get($key){ return $this->registry->get($key); }
public function __set($key,$value){ $this->registry->set($key, $value); }
}
class Applications {
private $registry;
function __construct($Registry){ $this->registry = $Registry; }
function __get($key){ return $this->registry->get($key); }
function __set($key,$val){ return $this->registry->set($key,$val); }
public function buildApplication(){
$this->application_page = 'current/page';
$application = new application($this->registry);
}
}
class Application extends Controller {
public function index(){
echo empty($this->application_page)?'yes':'no';
//Returns Yes
$page = $this->application_page;
echo empty($page)?'yes':'no';
//Returns No
}
}
Hopefully this helps?
Had a Typo, Registry's functions are not magic methods. Also $registry was declared in Applications.
The class probably didn't implement the magic __isset() method which is triggered by calling isset() or empty() on inaccessible properties.
Example:
Live demo I:
<?php
class Test {
private $a = '42';
public function __get($name) {
return $this->a;
}
}
$obj = new Test();
var_dump($obj->a); // string(2) "42"
var_dump(empty($obj->a)); // bool(true)
Implementing the __isset() method as follows (Live demo II) will yield the correct result:
public function __isset($name) {
if ($name == 'a') {
return true;
}
return false;
}
// ...
var_dump($obj->a); // string(2) "42"
var_dump(empty($obj->a)); // bool(false)
Here is a new version of your code implementing the __isset() methods: http://ideone.com/rJekJV
Change log:
Added __isset() method to Controller class which internally calls Registry::has().
Added has() method to Registy class.
[Only for testing: Object initialization and running Application::index() method.]
There was one issue (after updating your answer):
You haven't declared $registry as a member variable in the Applications class.
The code fails otherwise because it doesn't access the actual member variable (magic __set() method!)
Also, you had quite some redundancy in your code (not DRY). I hope this isn't production code ;)
I am currently digging into the basics of php class / constructor.
I understand how a constructor works but not why I should use it.
For example when I have a constructor like this:
function __construct($arg1, $arg2){
$this->name = $arg1;
$this->speed = $arg2;
}
Why should I use __constructor and not a simple callback like:
function foo($arg1,$arg2){
$this->name = $arg1;
$this->speed = $arg2;
}
Thank you
Doing
$obj = new Class($var1, $var2);
And
$obj = new Class($var1, $var2);
$obj->foo($var1, $var2);
Have the same end result
By forcing values to be passed on the constructor, class can define Mandatory values it should have in order to construct a class. As in the later case, one can ignore foo.
Having a method to initialize means, one ends up having different method names, foo, init etc, constructor avoids this
The constructor is always called on object instantiation and is a known pattern.
Your second example isn't (if it's intended to perform a similar initialisation role as the constructor).
<?php
class abc {
function __construct($arg1, $arg2){
echo $arg1.' '.arg2;
}
}
$obj = new abc('manish','jangir');
?>
It will print "manish jangir" automatically when the object is created
The main purpose is to keep your code clean. With placing your initialization in the constructor you can kan be sure the variable to be used in the other function will be in valid state for example :
class Foo{
private $number;
public function setNumber($number) {
$this->number = $number;
}
public function getNumber() {
if ($this->number=== null) {
throw new RuntimeException("The Number is Null !");
}
return number;
}
}
this is the class with constructor
class Foo{
private $number;
public function __construct($number) {
$this->number = $number;
}
public function getNumber() {
if ($this->number=== null) {
throw new RuntimeException("The Number is Null !");
}
return number;
}
}
with constructor you can be sure the number will be initialized. I hope my answer is clear enough but if you have another question about my answer feel free to ask in the comment :)
We have the following chaining:
$obj = new obj();
$obj->setname($params1)->setcolor($params2);
Is there a way to do the same chaining on one line, without creating a dummy function?
P.S: I want to skip the part where the constructor itself is on a new line. I want to construct the object and start the chaining on the same line. Something like this:
$obj = new obj()->setname($params1)->setcolor($params2);
Since PHP 5.4, class member access on instantiation has been added so you can do it like this:
$obj = (new obj())->setname($params1)->setcolor($params2);
In previous versions, like you I hate that you have to instantiate the object on one line and then start using it on another, so I have a global function _i() which looks like this:
function _i($i) { return $i; }
I use it like this:
_i(new Obj)->doThis($param)->doThat($param2);
Some people will find it ugly but PHP lacks language expression power, so it works for me :)
I use static functions of class for it.
class a{
static public function gets($args){
return new self($args);
}
public function do_other(){
}
}
a::gets()->do_other();
Usually there are more then I static method to different usages
Should be possible if you allways return the object itself in the function.
function setname($param) {
// set the name etc..
return $this;
}
You can also use PHP type hinting to make sure only the correct object is used as an argument
function instance(sdtClass $instance) { return $instance }
or as the static method using the class name
class CustomClass{
static public function gets(CustomClass $obj){
return $obj;
}
}
You can also use this technique from Singleton pattern (without using singleton pattern):
<?php
class test
{
public function __construct() {
}
public static function getInstance() {
return new test();
}
public function chain() {
echo 'ok';
}
}
// outputs 'ok'
$test = test::getInstance()->chain();
Sure is. Simplt return this at the end of each function, to return the object so your next chained function can use it.
<?php
class A
{
public __constructor()
{ .... }
public function B($params)
{
//Processing
return this;
}
public function C($params)
{
//Processing
return this;
}
public function D($params)
{
//Processing
}
}
$O = new A();
$O->B($params)->C($params)->D($params); //Will work because B and C return this
$O->B($params)->D($params)->C($params); //WILL NOT work because D doesn't return this
?>
So i was wondering if there is a way to method chain, when the initial method is a static function. Here is what I mean:
class foo
{
public static function a()
{
$foo = new foo;
return $foo->bar();
}
public function bar()
{
return $this;
}
public function b()
{
return 1;
}
}
print foo::a()->b();
EDIT
print foo::a()->b(); not print foo:a()->b();
Static Methods or Other Methods, as long as the method is returning an object either self or some other, the methods can be chained, with the same method you are attempting.
class foo {
public function __construct() {
}
public function create() {
// create something;
return $this;
}
public function performSomethingElse() {
// perform something
return $this;
}
}
$object = new foo;
$object -> create() -> performSomethingElse();
this line
print foo:a();
should be
print foo::a();
and you will not be able to return $this in a static method
it needs to be instantiated first:
$foo = new Foo();
print $foo->a()->b();
Only sort of an answer, and somewhat idiosyncratic:
But I would advise that you have your object accompanied by a factory procedure instead:
class foo { .... }
function foo() { return new foo; }
This might remove some of the confusion for you. And it even looks a bit nicer by avoiding the mix of static and object method calls:
foo()->bar()->b();
It basically externalizes the static function. And your object only implements the chainable methods which return $this, or actual results.