PHP - Argument with static variable in called function - php

I got a doubt while doing this:
class Logger {
public static $log_INFO = 'INFO';
public static $log_ERROR = 'ERROR';
public function log($logLevel, $param2, $param3) {
// Write log to some file
}
}
class Midea {
public function fn1 {
$logger = new Logger();
$logger->log(Logger::$log_INFO, 'some', 'some2');
}
}
Now my question is: Is there any way to make the log function in Logger class to accept only the static variables (any static variable) of Logger class? It should not accept any other string or integers as arguments.

My answer was based on the fact that $logLevel contains the name of a static class property.
If you use it as the updated example Logger::$INFO, that will pass the value string(4) "INFO" and this will not work. it needs to pass the value string(8) "log_INFO"
Yes, by using reflection:
public function log($logLevel, $param2, $param3) {
$reflection_property = new ReflectionProperty(get_called_class(), $logLevel);
if($reflection_property->isStatic()) {
// rest of the code
}
}
IMO this kind of enforcement is unnecessary, it adds both complexity and overhead to the code. And the benefits are small.
Coding your necessity like this seams more appropriate to me:
public static function $log_levels = array('INFO', 'ERROR');
public function log($log_level, $param2, $param3) {
if(in_array($log_level, static::$log_levels)) {
// code
}
}
The structure above opens up a neat opportunity:
public static function $log_levels = array(
'INFO' => array('Logger', 'handleInfoLogs'),
'ERROR' => array('Logger', 'handleErrorLogs')
);
public function log($log_level, $param2, $param3) {
if(array_key_exists($log_level, static::$log_levels)) {
return(static::$log_levels[$log_level]($param2, $param3));
}
}

What you are asking for is akin to enums in the Java world. Check this question on SO, which has some information on how you can implement similar concepts in PHP.
More specifically, you could implement what you are asking for like this:
class Logger {
const INFO = 1;
const ERROR = 2;
};
You could then use it in code like:
Logger::INFO
It isn't perfect, but I believe it is as close as it gets in PHP. To make it bullet-proof, you would have to employ some reflection to check the arguments passed in. This answer on SO has more information on how you can go about implementing it.

It's quite cumbersome but you could do this:
abstract class LoggerStatus
{
public function __toString()
{
return $this->status;
}
}
class LoggerStatusInfo extends LoggerStatus
{
protected $status = 'INFO';
}
class LoggerStatusError extends LoggerStatus
{
protected $status = 'ERROR';
}
class Logger {
public static $log_INFO;
public static $log_ERROR;
public function __construct()
{
self::$log_INFO = new LoggerStatusInfo();
self::$log_ERROR = new LoggerStatusError();
}
public function log(LoggerStatus $logLevel, $param2, $param3) {
// Write log to some file
}
}
I've never attempted this myself but I don't see any reason it wouldn't work. Personally, I'd go for something simpler.

Related

PHP OOP classes and functions

I am not sure how to name this, but here it goes. Lets suppose i have the following
class A {
public function aa() {
$this->bb();
}
public function bb() {
}
}
class B extends a {
}
class C {
__construct(B $service) {
$this->service = $service;
}
public function aa() {
$this->service->aa();
}
}
My call in code will be
$C = new C(new B());
$C->aa();
So this will basically execute A:aa() which is what i want. As you can see, in A::aa() AA::bb() is called.
What I need. When AA::bb() is called i want to execute some code defined in class C, but I am not allowed to change the A class. I can only change the B class or the C class.
My idea was to add a listener in the B class and overwrite the bb() function like this
class B extends a {
public $listener;
bb() {
parent::bb();
$this->listener();
}
}
class C {
__construct(B $service) {
$this->service = $service;
}
public function aa() {
$this->service->listener = function() { }
$this->service->aa();
}
}
But I don't like this idea a lot, doesn't look like a good one. What are my options here?
Again, I CANNOT change the A class and i can only call the C class.
PHP version is 5.3
You have two options. Extend or decorate.
First one would be kinda what you have already written, though, I would not use public visibility for the listener:
class Foo extends A {
private $listener;
public function setListener(callable $func) {
$this->listener = $func;
}
public function bb() {
call_user_func($this->listener);
return parent:bb();
}
}
In the example I passed the listener via setter injection, but you can also use constructor injection and pass the $listened in the overloaded __construct() method. When you extend a class, the "interface restriction" does not aply to the constructor's signature.
The other approach is to use a decorator:
class Foo {
private $target;
public function __construct(A $target) {
$this->target = $target;
}
public function bb($callback) {
$callback();
return $this->target->bb();
}
public function __call($method, $arguments) {
return call_user_func_array(
array( $this->target, $method ),
$arguments
);
}
}
The second approach would let you alter the interface.
Which option you pick depend on the exact functionality you actually need to implement. The decorator is a solution for, when you need drastic change in the objects behavior - for example, it is really good for adding access control.
I understand that you want to execute code in C after code in A completes. You cannot change A.
As written, C::aa calls A::aa, which calls A::bb and the stack unwinds. Why not just do the work in C::aa after the service call finishes?
class C {
public function aa() {
$this->service->aa();
// whatever you want to do
}
}
If, on the other hand, you need to call code after A::aa is called but before A::bb is called then the example you posted would suffice with clarity:
class B extends a {
public $listener;
public function bb() {
call_user_func($this->listener);
parent::bb();
}
}
Note the use of call_user_func, which is necessary for PHP 5.3 to call an anonymous function stored in a member variable.

Bad practice to expose private/protected methods during runtime

Okay I don't know if it's bad design but I feel a bit bad about doing the following:
abstract class A
{
abstract public function getCallable();
}
class B extends A
{
public function getCallable()
{
return array($this, 'doSomething');
}
protected function doSomething($param1, $param2, $param3)
{
// Do stuff here
}
}
The reason why B::doSomething is protected is that I don't like to expose this method because it should only be called from somewhere else in the code where I do a call_user_func() for the return value of B::getCallable.
You should be free to organize yourself in the subclasses of A. Without exposing anything to the outside. So the "API" won't change to the view outside of the subclasses of A.
So you should not be able to do something like:
$b = new B();
$b->doSomething($param1, $param2, $param3);
the only way to get B::doSomething executed should be over:
$b = new B();
call_user_func($b->getCallable());
So I'm thinking about how I could achieve that. One way I could think of is create a ReflectionMethod object from B::getCallable()'s return value and set it to accessable if the method is not public.
I don't like this solution it would work fine but is not that elegant:
class B extends A
{
public function getCallable()
{
return function($param1, $param2, $param3)
{
$this->doSomething($param1, $param2, $param3);
};
}
protected function doSomething($param1, $param2, $param3)
{
// Do stuff here
}
}
Any good suggestions or other ideas how to work around this?
Here is a more clear explanation of what i'm trying to articulate. In the below example you see that I come to realize there is no danger in exposing the doSomething function publicly, so I change it's scope instead of creating a clever intermediary function.
class One {
protected function doSomething( )
{
return 'hi';
}
}
class Two extends One {
public function doSomething( )
{
return parent::doSomething();
}
}
$Class = new Two();
echo $Class->doSomething();
Less is more, so KISS your code ;)

Is it possible to (somehow?) declare the format of a constructor in a PHP interface (or anything about it)?

I would like some feedback on my coding approach (i.e., whether it is appropriate or whether what I have done can be done in a perhaps better way):
I would like to create an interface to document that a constructor should have a specific format. Of course, if the interface only contains a constructor (and I was even surprised that PHP lets you put a constructor in an interface), the interface will have no effect (except for possibly documentation). Besides, PHP does not enforce the parameters of any callable to match, neither in number nor in type, and this is true of functions, methods, and constructors alike.
If you see how I have named my classes, you will realize what I am trying to do (: document that the constructor parameter must be a messager instance, too bad I could not do more to enforce this). Please let me know if my approach is OK and whether I can improve it.
class Messenger {
private $message;
function __construct($message = "Hello!") {
$this->message = $message;
}
public function getMessage() {
return $this->message;
}
}
With the above simple class in mind, I want to create an interface such as the following, but since we're dealing with a PHP constructor this should be useless?
interface MessengerAware {
function __construct($messenger);
}
class MessengerKnower implements MessengerAware {
private $messenger;
function __construct($messenger) {
$this->messenger = $messenger;
}
public function displayMessengerMessage() {
echo $this->messenger->getMessage();
}
}
I then want to enforce my interface in a class called Runner such as the following:
class Runner {
private $messengerAware;
function __construct($messengerAware) {
if (!is_a($messengerAware, 'MessengerAware')) {
die("I'm expecting an instance implementing the MessengerAware interface.");
}
$this->messengerAware = $messengerAware;
}
public function run() {
echo "I'm running.\n";
$this->messengerAware->displayMessengerMessage();
}
}
and finally run this code:
$messengerAware = new MessengerKnower(new Messenger());
$runner = new Runner($messengerAware);
$runner->run();
OUTPUT:
I'm running.
Hello!
Perhaps it's not possible, but the problem could be worked around using one (or more) factory methods:
Leave this unchanged:
class Messenger {
private $message;
function __construct($message = "Hello!") {
$this->message = $message;
}
public function getMessage() {
return $this->message;
}
}
This modification...
interface MessengerAware {
public static function create($messenger);
public function displayMessengerMessage();
}
and this one...
class MessengerKnower implements MessengerAware {
private $messenger;
public static function create($messenger) {
$messengerKnower = new MessengerKnower();
$messengerKnower->messenger = $messenger;
return $messengerKnower;
}
public function displayMessengerMessage() {
echo $this->messenger->getMessage();
}
}
Leave this unchanged...
class Runner {
private $messengerAware;
function __construct($messengerAware) {
if (!is_a($messengerAware, 'MessengerAware')) {
die("I'm expecting an instance implementing the MessengerAware interface.");
}
$this->messengerAware = $messengerAware;
}
public function run() {
echo "I'm running.\n";
$this->messengerAware->displayMessengerMessage();
}
}
Finally adjust this code:
$messengerAware = MessengerKnower::create(new Messenger());
$runner = new Runner($messengerAware);
$runner->run();
OUTPUT:
I'm running.
Hello!

Basic OOP architecture about an application with a single point of entry - PHP?

My application has a single point of entry let's call it index.php.
In index.php it instantiates a class like below;
final class Griff {
public $a, $b, $c, $d, $e;
public function __construct() {
spl_autoload_register(array($this, 'autoload',));
$this->a = 'a';
// blah blah blah
new RouterGriff($this);
}
private function autoload($name) {
// autoload function
}
}
new Griff();
You will notice that RouterGriff is instantiated inside Griff::__construct(), RouterGriff looks like below:
final class RouterGriff {
private $griff;
public function __construct(Griff $griff) {
$this->griff = $griff;
$this->griff->b = 'b';
$this->griff->c = 'c';
}
}
My question is as follows; you will notice I am setting variables for properties that are stored in Griff from RouterGriff as I want a registry kind of structure to my application but do not want to use a singleton.
Would it be better if I just had the properties set in RouterGriff instead of Griff? Or is passing Griff around to every class a valid way of doing things, considering my application could go 10 classes deep before it outputs anything?
I hope I made sense and thank you
EDIT
By the other way I ment doing it this way,
final class Griff {
public $a;
public function __construct() {
spl_autoload_register(array($this, 'autoload',));
$this->a = 'a';
// blah blah blah
new RouterGriff();
}
private function autoload($name) {
// autoload function
}
}
new Griff();
final class RouterGriff {
public $b, $c;
public function __construct() {
$this->b = 'b';
$this->c = 'c';
}
}
The answer to you question you are looking for is named "dependency injection" or "dependency injection container". This is a wide topic filling books. If you are interessted in this topic, I can suggest you: Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin)
If you're talking about global application settings (Which I think you are), just use constants.
They're global, but cannot be changed, and they are easy to write and use.
I dont think youre wrong about injecting Griff into everything that is fine. As far as what classes get which propertied i think the depends on the nature of the properties. For example i would have included a GriffRequest in this example which is where you would access/store the parsed params the router supplied for example:
final class Griff {
protected $_request,
$_response,
$_router;
public function __construct() {
spl_autoload_register(array($this, 'autoload',));
$this->_request = new GriffRequest($this);
$this->_response = new GriffResponse($this);
$this->_router = new RouterGriff($this);
}
private function autoload($name) {
// autoload function
}
public function getRequest() { return $this->_request; }
public function getResponse() { return $this->_response; }
public function getRouter() { return $this->_router; }
}
final class RouterGriff {
protected $_application;
public function __construct(Griff $application) {
$this->_application = $application;
}
public function route() {
// do stuff with request
// assign params
$request = $application->getRequest();
$request->a = 'value';
$request->b = 'value2';
}
}
If you make Griff store a ref to all the important submodules of the app and then inject Griff into all those modules then you can get access to everything you might need by going through Griff at some point. You can always make convenience getters if you feel the chain is to long.

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