PHP Magic methods - php

My question revolves arround magic methods.
This is a little example:
$context = new Context('Entities.xml');
$user_array = $context->Users;
$user = $context->Users->find(array('name' => 'John Smith'));
The second line returns an array with all user objects in it. The third line returns only the User object of the user called John Smith.
I wondered if this would be possible, the tricky part is that I don't know the properties of the Context class. They are generated from an xml file the user supplies upon instantiation and are accessable through magic getters and setters.
Context example(not complete, just to give an idea):
class Context {
private $path, $entities;
public function __construct($path) {
$this->path = $path;
}
public function __get($name) {
return $entities[$name];
}
public function __set($name, $arg) {
$entities[$name] = $arg;
}
}

Because I really needed a solution I implemented the following solution.
The getters of the Context class return a ResultLayer class that handles the results.
Example:
class ResultLayer implements IteratorAggregate {
public $data = array();
private $entity, $context;
public function __construct($context, $entity) {
$this->context = $context;
$this->entity = $entity;
}
public function getIterator() {
return new ArrayIterator($this->data);
}
public function get($index) {
return $this->data[$index];
}
public function toArray() {
return $this->data;
}
public function find($properties) {
return $this->context->getEntity($this->entity, $properties);
}
}
I have implemented the IteratorAggregate interface so that you can use a foreach loop to for example go through $Context->Users this makes the code more readable.
If anyone has a better approach, I'm still open to it. Any help is much appreciated!

Related

return type of container from subclass

I have a PHP library which I don't want to edit, and implement to my code by extending/overriding some methods. But I'm stuck with chainability. For example:
class MomentPHP extends Moment {
public $uniqueSettings;
public function formatJS(){
return parent::format($this->uniqueSettings);
}
}
class Moment {
public function startOf(){
//some code
return $this;
}
}
I want to do this:
$momentphp = new MomentPHP();
$dateStart = $momentphp->startof('month')->formatJs();
And the way to do this is overriding all the methods in the child class inside MomentPHP to return itself.
Is there any other simple way to do this? like using _call or something?
Edit: Found one way to do this:
Remove the inheritance,
Create a instance variable of parent class,
use __call method to switch between classes.
Like this:
class MomentPHP {
private $instance = null;
public $uniqueSettings;
public function __construct(){
$this->instance = new Moment();
}
public function __call($method,$args){
if(in_array($method, get_class_methods($this))){
call_user_func(array($this,$method),$args);
else
call_user_func(array($this->instance,$method),$args);
return $this;
}
public function formatJS(){
return $this->instance->format($this->uniqueSettings);
}
}
class Moment {
public function startOf(){
//some code
return $this;
}
}
Is there any better way?
One proper way to do this is:
class MomentPHP {
private $instance = null;
public $uniqueSettings;
public function __construct(){
$this->instance = new Moment();
// settings etc.
}
public function __call($method,$args){
$result = NULL;
if(in_array($method, get_class_methods($this))){
$result = call_user_func(array($this,$method),$args);
else
$result = call_user_func(array($this->instance,$method),$args);
if($result instanceof Moment)
$this->instance = $result;
return $this;
}
public function format(){
return $this->instance->format($this->uniqueSettings);
}
}
Updating the instance from the method result is the key operation, and using $this instead of $this->instance allows you to use the extender class in every call. So you can override the function while using other methods in the parent class with chaining ability.

Array of objects and instance of a class

What would be the best practice to do this :
class AwesomeClass {
// Code
public function test()
{
foreach($objects->values as $v)
{
New SuperClass($v);
}
return $objects;
}
}
class SuperClass {
public function __construct($arg2)
{
return trim($arg2);
}
}
$rule_the_world = New AwesomeClass($arg1);
$king = $rule_the_world->test();
The previous code is obviously not working, I think I'm missing some major point of PHP OO.
It's very difficult to decipher what you're asking for, and the code you have is not recoverable.
Code Errors
There are several errors in your code that are illogical:
AwesomeClass has no constructor.
This makes passing arg1 to new AwesomeClass meaningless
arg1 is never initialized
In AwesomeClass::test(), objects is never initialized and has no member value.
You will get a warning since it's not traversable
New SuperClass (should be new, per standards) does nothing.
__construct() cannot return a value.
What You May Want
What I think you're going for is something like this:
class AwesomeClass implements IteratorAggregate {
private $arg1;
public function __construct(array $arg1) {
$this->arg1 = $arg1;
}
public function getIterator() {
return new ArrayIterator($this->arg1);
}
}
class SuperClass {
private $arg2;
public function __construct($arg2) {
$this->arg2 = $arg2;
}
public function __toString() {
return "$this->arg2\n";
}
}
$rule_the_world = new AwesomeClass(array('one', 'two', 'three'));
foreach ($rule_the_world as $sc) {
$sc = new SuperClass($sc);
echo $sc;
}
Note that it is redundant to create an ArrayIterator instance when arg1 must already be an array, this is just an example.

How can I create a singleton in PHP?

I'm using PDT and Aptana on Eclipse Indigo with PHP 5.3 and I want to create a singleton in a class.
By singleton, I mean I want to just have one instance of that object, and for other objects or classes to get that single instance via a function that returns that object (so this would mean I'm trying to create an object within the class that defines that object, ie: creating objA within the class objA)
I understand you can't just go a head and do this:
public $object = new Object();
with in a class definition, you have to define it in the constructor.
How can I go ahead and do this? I'm coming from Java, so it could be I'm confusing some basic stuff. Any help is greatly appreciated. Here's the code:
<?php
class Fetcher{
private static $fetcher = new Fetcher(); //this is where I get the unexpected "new" error
static function getFetcherInstance(){
return $this->$fetcher;
}
}
?>
Solved! Thanks for all the help guys!
try this:
<?php
class myclass{
private static $_instance = null;
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new myclass();
}
return self::$_instance;
}
}
?>
and call it with:
<?php
$obj = myclass::getInstace();
?>
You cannot assign a class property in PHP like that. It must be a scalar, or array value, or the property must be set in a method call.
protected static $fetcher;
static function getFetcherInstance(){
if (!self::$fetcher) {
self::$fetcher = new Fetcher();
}
return self::$fetcher;
}
Also, notice that I did not use $this->, as that only works for object instances. To work with static values you need to use self:: when working within the class scope.
You might want to just read common design patterns on the php site. There are pretty good examples with good documentation:
http://www.php.net/manual/en/language.oop5.patterns.php
Else, a singleton is simply a method that returns one single instance of itself:
class MySingletonClass {
private static $mySingleton;
public function getInstance(){
if(MySingletonClass::$mySingleton == NULL){
MySingletonClass::$mySingleton = new MySingletonClass();
}
return MySingletonClass::$mySingleton;
}
}
Building on #periklis answer you might want separate singletons for different application scopes. For example, lets say you want a singleton of a database connection - fine. But what if you have TWO databases you need to connect too?
<?php
class Singleton
{
private static $instances = array();
public static function getInstance($name = 'default')
{
if ( ! isset(static::$instances[$name]))
{
static::$instances[$name] = new static();
}
return static::$instances[$name];
}
}
Class DB extends Singleton {}
$db_one = DB::getInstance('mysql');
$db_two = DB::getInstance('pgsql');
Alse define __clone method
class Fetcher {
protected static $instance;
private function __construct() {
/* something */
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new Fetcher();
}
return self::$instance;
}
private function __clone() {
/* if we want real singleton :) */
trigger_error('Cannot clone', E_USER_ERROR);
}
}
Basically implementing a singleton pattern means writing a class with a private constructor and a static method to build itself. Also check PHP site for it: http://www.php.net/manual/en/language.oop5.php and http://it2.php.net/manual/en/book.spl.php
class A {
protected $check;
private function __construct($args) {
}
static public function getSingleton($args) {
static $instance=null;
if (is_null($instance)) {
$instance=new A();
}
return $instance;
}
public function whoami() {
printf("%s\n",spl_object_hash($this));
}
}
$c=A::getSingleton("testarg");
$d=A::getSingleton("testarg");
$c->whoami(); // same object hash
$d->whoami(); // same object hash
$b= new A("otherargs"); // run time error
<?php
class MyObject {
private static $singleInstance;
private function __construct() {
if(!isset(self::$singleInstance)) {
self::$singleInstance = new MyObject;
}
}
public static function getSingleInstance() {
return self::$singleInstance;
}
}
?>
class MyClass {
private static $instance;
public static function getInstance() {
if( !isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
}
Then call get instance using
MyClass::getInstance();

Return a reference to an instance of an object in PHP

I have a singleton factory and would like it to return a reference to the object instance so that I can use the singleton factory to destroy the instance and not have instances elsewhere in my code to survive.
Example of what I would like to be able to do:
$cat = CatFactory::getInstance();
$cat->talk(); //echos 'meow'
CatFactory::destructInstance();
$cat->talk(); //Error: Instance no longer exists
This could work:
<?php
class FooFactory
{
private static $foo;
private function __construct()
{
}
public static function getInstance()
{
return self::$foo ? self::$foo : (self::$foo = new FooFactory());
}
public static function destroyInstance()
{
self::$foo = null;
}
public function __call($fn, $args)
{
if (!method_exists(self::$foo, $fn) || $fn[0] == "_")
throw new BadMethodCallException("not callable");
call_user_func_array(array(self::$foo, $fn), $args);
}
# function hidden since it starts with an underscore
private function _listen()
{
}
# private function turned public by __call
private function speak($who, $what)
{
echo "$who said, '$what'\n";
}
}
$foo = FooFactory::getInstance();
$foo->speak("cat", "meow");
$foo->_listen(); # won't work, private function
FooFactory::destroyInstance();
$foo->speak("cow", "moo"); # won't work, instance destroyed
?>
Obviously it is a hack.
Based on the documentation for unset, I do not think that is possible. You cannot actually destroy an object, only a handle to it. If other variables are around that still hold a reference, the object will continue to live on.
You can accomplish what you want by having your Cat object enforce a private $destroyed property. PHP 5 passes objects by reference by default, so you don't have to worry about that part.
A work around would be creating a cat class
class cat
{
public $cat;
public function __construct()
{
$this->cat = CatFactory::getInstance();
}
public function __destruct()
{
CatFactory::destructInstance();
}
}
$cat = new cat();
$cat->cat->talk();
$cat->cat->talk();

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