PHP chained class methods merged with other classes - php

I start with an example.
Mold class
<?php
class Mold {
public $current;
function merge($first, $second) {
$this->current = $first . $second;
return $this;
}
function phone() {
$this->current = '' . $this->current . '';
return $this;
}
function __toString() {
return $this->current;
}
}
function mold() {
return new Mold();
}
In action
This works as expected.
echo mold()->merge('sada', 'asdsa')->phone();
Problem
I have one or more classes with methods that wants to be available as well.
Plugin class
class MyPlugin {
function link() {
// Code
}
function currency($number) {
// Code
}
}
class MyPlugin2 {
// Other methods
}
Dream code
The exact change of events below may not make any sense. Anyway, what I intend to do is the following.
echo mold()->merge('sada', 'asdsa')->currency(45)-link();
Call mold() which creates a new instance of the Mold class.
Chain merge() which is used from the Mold class.
currency() or link() method does not exist in the Mold class. Instead they should be loaded from one of the plugins.
In conclusion
I know I can extend a class but it does not really solve the problem because there can be more than one plugin classes.
I know I can create instances of the plugin classes, but they somehow need to be aware by the Mold class.
Append methods to a class comes to mind as well as merge classes.

Technically you could use __call() for this. Your "main" class could contain/track a set of plugin instances and any unknown method call could be looked up and delegated, if available in a plugin.
I wouldn't recommend this though. Fluent APIs are often brittle and can get inconvenient even with one class. Involving multiple classes in fluent calls might quickly to add up to a confusing mess. A mess which even IDE can't help you (or another person working with code) with since it has no clue about all the magic happening.
I would highly recommend to look into alternative patterns for an API like this.

A concept that you might try to apply is the Composition Over Inheritance.
A solution could be to compose your current Moldobject and pass the two plugins instances so that it could use them on its methods, like so:
<?php
class Mold {
public $current;
public function __construct(Plugin1 $plugin1, Plugin2 $plugin2) {
$this->plugin1 = $plugin1;
$this->plugin2 = $plugin2;
}
public function merge($first, $second) {
$this->current = $first . $second;
return $this;
}
public function phone() {
$this->current = '' . $this->current . '';
return $this;
}
public function __toString() {
return $this->current;
}
public function link() {
$this->plugin1->link();
return $this;
}
}
function mold() {
return new Mold(new Plugin1(), new Plugin2());
}
Another solution could be to create a MoldHandler class of some sort, which has a Mold object as a property together with the other plugins, which could be used inside of this directly.
public class MoldHanlder {
protected $mold;
protected $plugin1;
protected $plugin2;
public function __contruct($mold, $plugin1, $plugin2) {
$this->mold = $mold;
$this->plugin1 = $plugin1;
$this->plugin2 = $plugin2;
}
public function merge() {
$this->mold = $this->mold->merge();
return $this;
}
public function link() {
$foo = $this->plugin1->method();
return $this;
}
...
}
You could also just instantiate the classes inside the construct, but if you are planning about writing unit tests, dependency injection is the way to go
I think that the first approach is way better because you avoid yourself to re-write some code of the Mold class, which is unnecessary
N.B. this is a really raw solution proposed just to let you better understand the idea I would opt for
cheers

Interesting ideas.
The one that solves the problem like I had in my mind was to use traits like #vivek_23 suggested in a comment.
Here is a complete example of how it works
<?php
// Core methods
class MoldCore {
public $current;
function merge($first, $second) {
$this->current = $first . $second;
return $this;
}
function phone() {
$this->current = '' . $this->current . '';
return $this;
}
function __toString() {
return $this->current;
}
}
// Plugin 1
trait Plugin1 {
public function yaay() {
$this->current .= ' Plugin1 yaay';
return $this;
}
}
// Plugin 2
trait Plugin2 {
public function hello() {
$this->current = str_replace('Plugin1', 'Modified', $this->current);
return $this;
}
public function world() {
$this->current .= ' Plugin2 world';
return $this;
}
}
// Glue plugins with MoldCore
class Mold extends MoldCore {
use Plugin1, Plugin2;
}
$mold = new Mold();
echo $mold->merge('sada', 'asdsa')->phone()->yaay()->hello();

Related

How to return function values when using method chaining in PHP?

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.

php oo programming parent to instantiate child [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What exactly is late-static binding in PHP?
I would like to build an abstract class that will instantiate it's child class using a static function.
<?php
class A
{
protected $value;
public function __construct($value)
{
$this->value = $value;
}
public static function create($value)
{
/* A must not know about B!
* I give that example for the context understanding */
return **new B**($value);
}
}
class B extends A
{
}
Of course A has not to know about B.. I do not know if it is possible and I do not want to have to implement the create function in all my 170 subclasses...
Do you think I should use a Factory that would return the instance of one of my 170 classes? That is cumbersome and not very maintainable..
I found out a solution, but is it ok?
<?php
class A
{
protected $value;
public function __construct($value)
{
$this->value = $value;
}
public static function create($value)
{
$child = get_called_class();
return new $child($value);
}
}
I tested it, it's working OK it's really useful in my context.. I do not know if it's ugly, because the parent class doesn't know about the child class, and if the Child class does not exists, the caller will not be allowed to call the create function..
Now, for my practical test, I use:
$graph->setBgcolor(Bgcolor::create('yellow')->valid());
with
public function setBgcolor(Bgcolor $bgcolor)
A successful implementation could be like this
class A
{
protected $value;
public static function create($value)
{
$type = "NotDivisible";
for ($i = 2; $i <= 10; $i++) {
if($value % $i == 0)
{
$type = "DivisibleBy".$i;
break;
}
}
$instance = new $type;
$instance->value = $value;
return $instance;
}
}
public class DivisibleBy2 extends A
{
}
public class DivisibleBy3 extends A
{
}
public class DivisibleBy5 extends A
{
}
...
so on.... (I haven't compiled it, sorry for syntax error)
Now as you know static method has nothing to do with the instance of object created because this is why we create a static method at the first place.
So you can put them in any other class and congrats you have implemented the factory design pattern.
However this is not a good implementation.
Another solution would NOT to use the static function and use basic new...
<?php
class A
{
protected $value;
public function __construct($value)
{
$this->value = $value;
}
}
Now, for my practical test, I use:
$graph->setBgcolor(new Bgcolor('yellow'));
with
public function setBgcolor(Bgcolor $bgcolor)
I still think that
Bgcolor::create('yellow')->foo()
looks better than
(new Bgcolor('yellow'))->foo()

PHP class methods cascading

I have seen many php scripts in which you can cascade class method to produce a specific functionality for example as in codeigniter when you load a view you would type
<?php
class Posts extends Controller
{
function index() {
$this->load->view("BlaBla");
}
}
?>
I am not completely sure if these are cascaded methods or what butI don't have enough experience to hack the codeigniter core and figure out myself.
Can anyone tell me how to do this or guide me to some tutorial or so
Every method must return its class instance, like:
class A{
function B(){
//do stuff
return $this;
}
function C(){
//do stuff
return $this;
}
function D(){
return $this->B()->C()->B()->B()->C();
}
}
or you can build your own cahining class to chain anything:
class Ch{
static private $i;
public static function i($arg){
if (!self::$i instanceof self){
self::$i = new self();
}
self::$i->data=$arg;
return self::$i;
}
function __call($name,$args){
array_unshift($args,$this->data);
$this->data=call_user_func_array($name,$args);
return $this;
}
function get(){
return $this->data;
}
}
echo Ch::i('Hello world')->trim('Hld')->str_repeat(5)->substr(5,7)->strtoupper()->get();

How to add a method to an existing class in PHP?

I'm using WordPress as a CMS, and I want to extend one of its classes without having to inherit from another class; i.e. I simply want to "add" more methods to that class:
class A {
function do_a() {
echo 'a';
}
}
then:
function insert_this_function_into_class_A() {
echo 'b';
}
(some way of inserting the latter into A class)
and:
A::insert_this_function_into_class_A(); # b
Is this even possible in tenacious PHP?
If you only need to access the Public API of the class, you can use a Decorator:
class SomeClassDecorator
{
protected $_instance;
public function myMethod() {
return strtoupper( $this->_instance->someMethod() );
}
public function __construct(SomeClass $instance) {
$this->_instance = $instance;
}
public function __call($method, $args) {
return call_user_func_array(array($this->_instance, $method), $args);
}
public function __get($key) {
return $this->_instance->$key;
}
public function __set($key, $val) {
return $this->_instance->$key = $val;
}
// can implement additional (magic) methods here ...
}
Then wrap the instance of SomeClass:
$decorator = new SomeClassDecorator(new SomeClass);
$decorator->foo = 'bar'; // sets $foo in SomeClass instance
echo $decorator->foo; // returns 'bar'
echo $decorator->someMethod(); // forwards call to SomeClass instance
echo $decorator->myMethod(); // calls my custom methods in Decorator
If you need to have access to the protected API, you have to use inheritance. If you need to access the private API, you have to modify the class files. While the inheritance approach is fine, modifiying the class files might get you into trouble when updating (you will lose any patches made). But both is more feasible than using runkit.
An updated way for 2014 that copes with scope.
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
Eg:
class stdObject {
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
}
$obj = new stdObject();
$obj->test = function() {
echo "<pre>" . print_r($this, true) . "</pre>";
};
$obj->test();
You can use the runkit extension for this, but you should really consider regular inheritance instead.
See runkit_method_add.
No you can't dynamically change a class during runtime in PHP.
You can accomplish this by either extending the class using regular inheritance:
class Fancy extends NotSoFancy
{
public function whatMakesItFancy() //can also be private/protected of course
{
//
}
}
Or you could edit the Wordpress source files.
I'd prefer the inheritance way. It's a lot easier to handle in the long run.

Faking method attributes in PHP?

Is it possible to use the equivalent for .NET method attributes in PHP, or in some way simulate these?
Context
We have an in-house URL routing class that we like a lot. The way it works today is that we first have to register all the routes with a central route manager, like so:
$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));
Whenever a route is encountered, the callback method (in the cases above they are static class methods) is called. However, this separates the route from the method, at least in code.
I am looking for some method to put the route closer to the method, as you could have done in C#:
<Route Path="admin/test/">
public static void SomeMethod() { /* implementation */ }
My options as I see them now, are either to create some sort of phpDoc extension that allows me to something like this:
/**
* #route admin/test/
*/
public static function SomeMethod() { /* implementation */ }
But that would require writing/reusing a parser for phpDoc, and will most likely be rather slow.
The other option would be to separate each route into it's own class, and have methods like the following:
class CAdminTest extends CRoute
{
public static function Invoke() { /* implementation */ }
public static function GetRoute() { return "admin/test/"; }
}
However, this would still require registering every single class, and there would be a great number of classes like this (not to mention the amount of extra code).
So what are my options here? What would be the best way to keep the route close to the method it invokes?
This is how I ended up solving this. The article provided by Kevin was a huge help. By using ReflectionClass and ReflectionMethod::getDocComment, I can walk through the phpDoc comments very easily. A small regular expression finds any #route, and is registered to the method.
Reflection is not that quick (in our case, about 2,5 times as slow as having hard-coded calls to RegiserRoute in a separate function), and since we have a lot of routes, we had to cache the finished list of routes in Memcached, so reflection is unnecessary on every page load. In total we ended up going from taking 7ms to register the routes to 1,7ms on average when cached (reflection on every page load used 18ms on average.
The code to do this, which can be overridden in a subclass if you need manual registration, is as follows:
public static function RegisterRoutes()
{
$sClass = get_called_class(); // unavailable in PHP < 5.3.0
$rflClass = new ReflectionClass($sClass);
foreach ($rflClass->getMethods() as $rflMethod)
{
$sComment = $rflMethod->getDocComment();
if (preg_match_all('%^\s*\*\s*#route\s+(?P<route>/?(?:[a-z0-9]+/?)+)\s*$%im', $sComment, $result, PREG_PATTERN_ORDER))
{
foreach ($result[1] as $sRoute)
{
$sMethod = $rflMethod->GetName();
$oRouteManager->RegisterRoute($sRoute, array($sClass, $sMethod));
}
}
}
}
Thanks to everyone for pointing me in the right direction, there were lots of good suggestions here! We went with this approach simply because it allows us to keep the route close to the code it invokes:
class CSomeRoutable extends CRoutable
{
/**
* #route /foo/bar
* #route /for/baz
*/
public static function SomeRoute($SomeUnsafeParameter)
{
// this is accessible through two different routes
echo (int)$SomeUnsafeParameter;
}
}
Using PHP 5.3, you could use closures or "Anonymous functions" to tie the code to the route.
For example:
<?php
class Router
{
protected $routes;
public function __construct(){
$this->routes = array();
}
public function RegisterRoute($route, $callback) {
$this->routes[$route] = $callback;
}
public function CallRoute($route)
{
if(array_key_exists($route, $this->routes)) {
$this->routes[$route]();
}
}
}
$router = new Router();
$router->RegisterRoute('admin/test/', function() {
echo "Somebody called the Admin Test thingie!";
});
$router->CallRoute('admin/test/');
// Outputs: Somebody called the Admin Test thingie!
?>
Here's a method which may suit your needs. Each class that contains routes must implement an interface and then later loop through all defined classes which implement that interface to collect a list of routes. The interface contains a single method which expects an array of UrlRoute objects to be returned. These are then registered using your existing URL routing class.
Edit: I was just thinking, the UrlRoute class should probably also contain a field for ClassName. Then $oRouteManager->RegisterRoute($urlRoute->route, array($className, $urlRoute->method)) could be simplified to $oRouteManager->RegisterRoute($urlRoute). However, this would require a change to your existing framework...
interface IUrlRoute
{
public static function GetRoutes();
}
class UrlRoute
{
var $route;
var $method;
public function __construct($route, $method)
{
$this->route = $route;
$this->method = $method;
}
}
class Page1 implements IUrlRoute
{
public static function GetRoutes()
{
return array(
new UrlRoute('page1/test/', 'test')
);
}
public function test()
{
}
}
class Page2 implements IUrlRoute
{
public static function GetRoutes()
{
return array(
new UrlRoute('page2/someroute/', 'test3'),
new UrlRoute('page2/anotherpage/', 'anotherpage')
);
}
public function test3()
{
}
public function anotherpage()
{
}
}
$classes = get_declared_classes();
foreach($classes as $className)
{
$c = new ReflectionClass($className);
if( $c->implementsInterface('IUrlRoute') )
{
$fnRoute = $c->getMethod('GetRoutes');
$listRoutes = $fnRoute->invoke(null);
foreach($listRoutes as $urlRoute)
{
$oRouteManager->RegisterRoute($urlRoute->route, array($className, $urlRoute->method));
}
}
}
I'd use a combination of interfaces and a singleton class to register routes on the fly.
I would use a convention of naming the router classes like FirstRouter, SecondRouter and so on. This would enable this to work:
foreach (get_declared_classes() as $class) {
if (preg_match('/Router$/',$class)) {
new $class;
}
}
That would register all declared classes with my router manager.
This is the code to call the route method
$rm = routemgr::getInstance()->route('test/test');
A router method would look like this
static public function testRoute() {
if (self::$register) {
return 'test/test'; // path
}
echo "testRoute\n";
}
The interfaces
interface getroutes {
public function getRoutes();
}
interface router extends getroutes {
public function route($path);
public function match($path);
}
interface routes {
public function getPath();
public function getMethod();
}
And this is my definition av a route
class route implements routes {
public function getPath() {
return $this->path;
}
public function setPath($path) {
$this->path = $path;
}
public function getMethod() {
return $this->method;
}
public function setMethod($class,$method) {
$this->method = array($class,$method);
return $this;
}
public function __construct($path,$method) {
$this->path = $path;
$this->method = $method;
}
}
The Router manager
class routemgr implements router {
private $routes;
static private $instance;
private function __construct() {
}
static public function getInstance() {
if (!(self::$instance instanceof routemgr)) {
self::$instance = new routemgr();
}
return self::$instance;
}
public function addRoute($object) {
$this->routes[] = $object;
}
public function route($path) {
foreach ($this->routes as $router) {
if ($router->match($path)) {
$router->route($path);
}
}
}
public function match($path) {
foreach ($this->routes as $router) {
if ($router->match($path)) {
return true;
}
}
}
public function getRoutes() {
foreach ($this->routes as $router) {
foreach ($router->getRoutes() as $route) {
$total[] = $route;
}
}
return $total;
}
}
And the self register super class
class selfregister implements router {
private $routes;
static protected $register = true;
public function getRoutes() {
return $this->routes;
}
public function __construct() {
self::$register = true;
foreach (get_class_methods(get_class($this)) as $name) {
if (preg_match('/Route$/',$name)) {
$path = call_user_method($name, $this);
if ($path) {
$this->routes[] = new route($path,array(get_class($this),$name));
}
}
}
self::$register = false;
routemgr::getInstance()->addRoute($this);
}
public function route($path) {
foreach ($this->routes as $route) {
if ($route->getPath() == $path) {
call_user_func($route->getMethod());
}
}
}
public function match($path) {
foreach ($this->routes as $route) {
if ($route->getPath() == $path) {
return true;
}
}
}
}
And finally the self registering router class
class aRouter extends selfregister {
static public function testRoute() {
if (self::$register) {
return 'test/test';
}
echo "testRoute\n";
}
static public function test2Route() {
if (self::$register) {
return 'test2/test';
}
echo "test2Route\n";
}
}
the closest you can put your path to the function definition (IMHO) is right before the class definition. so you would have
$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));
class CTest {
public static function SomeMethod() {}
}
and
$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
class CAdmin {
public static function SomeMethod() {}
public static function SomeOtherMethod() {}
}
There is a proposal for this, it was declined. See the rfc here:
Attributes RFC at php.net
My solution to this desire is something like this:
abstract class MyAttributableBase{
protected static $_methodAttributes=[];
public static function getMethodAtributes(string $method):?array{
if( isset(self::$_methodAttributes[$method])){
return self::$_methodAttributes[$method];
}
return null;
}
protected static function setMethodAttributes(string $method, array $attrs):void{
self::$_methodAttributes[$method] = $attrs;
}
}
class MyController extends MyAttributableBase{
protected static function getMethodAtributes(string $method):?array{
switch( $method ){
case 'myAction':
return ['attrOne'=>'value1'];
default:
return parent::getMethodAttributes($method);
}
}
}
Usage:
$c = new MyController();
print $c::getMethodAttributes('myAction')['attrOne'];
You can of course use it from within a base class method to do "routing" stuff in this case, or from a routing class that operates on "MyAttributableBase" objects, or anywhere else you would want to inspect this attached metadata for any purpose. I prefer this "in-code" solution to using phpDoc. Note I didn't attempt to test this exact code but it is mentally copied from a working solution. If it doesn't compile for some small reason it should be easy to fix and use. I have not figured out a way to cleanly put the attributes near the method definition. Using this implementation in the base you COULD set the attributes within the method (myAction in this case) as the first code to execute, but it would not be a static attribute, it would get reset at each invocation. You could add code to additionally ensure it is only set once but that's just extra code to execute and maybe is not better. Overriding the get method allows you to set the info once and refer to it once, even though it's not that close to the method definition. Keeping the static array in the base does allow some flexibility if there are cases for adding or changing metadata at runtime. I could be possible to use something like phpDoc and a static constructor to parse that when the first class is created to populate the static metadata array. I haven't found a solution that is awesome but the one I'm using is adequate.

Categories