Pass a static function of an abstract class as a callback function - php

Let's say we've implemented a abstract class which holds some tools. For example a mail function:
abstract class Tools {
public static function sendMail () {
// do some magic mail sending
}
}
Now we have an index.php file, which uses the framework Flight for instance (doesn't mind which framework in particular).
In this index.php we define some routes and assign callback functions to be called if a certain route is requested.
Flight::route('POST /mail', Tools::sendMail);
If I try this code PHP returns this exception:
Fatal error: Undefined class constant 'sendMail'
Is there a way to pass a function like this in PHP?

Use a callable:
public static function route($method, callable $callable)
{
$callable();
}
Flight::route('POST /mail', function () { Tools::sendMail(); });

You can use this:
[__CLASS__,'MethodName']
if the callback is made in the same class or
[Class::class,'MethodName']
if it is not.

You'll need to pass 2 things in order to reference a static class method: The name of the class, and the name of the static function to call
//define the method
function callStaticClassMethod($className, $funcName) {
$className::$funcName();
}
//call the method
callStaticClassMethod('Tools', 'sendMail');
I'm not aware of any other way to directly reference a static function.

Related

PHP call class variable / property with stored closure

So I am making a Magento module in PHP. I want my logging to be consistent across all classes. My idea was to define a method in the helper class and call it. However being the pre-optimizer that I am, I figure making multiple calls to a class via the Mage::Helper() method to be more expensive than it needs to be, especially since most of my logging in singleton models anyways. So my now my idea is to use closures, define my method in the helper, make one call to the helper and register the method in a class variable.
class Comp_Mod_Helper_Data extends Mage_Core_Helper_Abstract {
public function getLogger() {
return function ($obj, $type= Zend_Log::DEBUG) {
Mage::log($obj, $logType, 'comp-mod.log', true);
};
}
}
Use:
class Comp_Mod__IndexController extends age_Core_Controller_Front_Action {
private $_log;
protected function _construct() {
$this->_log = Mage::Helper('mod')->getLogger();
}
}
However while it works ... it is not great to use. I'm either stuck doing:
$log = $this->_log;
$log('hello world');
// one awkward liner
($this->_log)('hello world');
While neat that it works is not readable nor standard, i.e. confusing!. The error that it get when using $this->_log('hello world'); is that the method does not exist. I assume because PHP is looking for a method call when using the syntax $this->method();
I do understand that A) I could just suck it up and use Mage::Helper everywhere, and B) that I could store the helper object in a variable and call like $this->helper->log(), and C) that static variables work, see PHP closure as static class variable
So, is there a way to get a non-static class variable to call the closure instead of looking for a non-existing method?
You could make use of the __call magic method:
class Comp_Mod__IndexController extends age_Core_Controller_Front_Action {
public function __call($method, array $args)
{
switch ($method)
{
case '_log':
return call_user_func_array(Mage::Helper('mod')->getLogger(), $args);
}
return null;
}
}
Then use it like you wanted to:
$this->_log('string to log');

Register static class method as shutdown function in PHP

In PHP is it possible to register a shutdown function (with register_shutdown_function()) which is a static method implemented in a class? I mean something like this:
//index.php
require_once("modules/Analyzer/Analyzer.php");
register_shutdown_function("Analyzer::log_shutdown");
//Analyzer.php
class Analyzer {
...
public static function log_shutdown(){
// do some awesome stuff
Analyzer::screenshot();
}
}
The first argument passed to register_shutdown_function is of the type callable. A callable static method looks like this:
register_shutdown_function(array('Analyzer', 'log_shutdown'));
As of PHP 5.2.3 it may also look exactly like what you originally posted
Can use an anonymous function:
register_shutdown_function(function(){
Analyzer::log_shutdown();
});
also, in analyzer you can use a call to the same class like this:
class Analyzer{
public static function log_shutdown(){
self::screenshot();
}
}
Eventually via namespace:
register_shutdown_function(function(){
\app\modules\Analyzer\Analyzer::log_shutdown();
});
or
register_shutdown_function(['\app\modules\Analyzer\Analyzer', 'log_shutdown']);
register_shutdown_function(["\\app\\modules\\Analyzer\\Analyzer", "log_shutdown"]);

Accessing the variables from a PHP Anonymous Function

I have the following class with static variables. How can I access the static functions of a class from within an anonymous PHP function?
class MyClass {
public static function MyFunction(mylocalparam){
MyStaticClass:MyStaticMethod(function(myparam) use(mylocalparam){
MyClass::MyFunction2(mylocalparam);
});
}
private static function MyFunction2(someobject){
}
}
I am having trouble accessing the function "MyFunction2" from within the anonymous class. Could you please advice on how to rectify this?
Not going to happen. You need to make the static function public. The anonymous function doesn't run inside the scope of MyClass, and therefore doesn't have access to private methods contained within it.
Statically is not possible, but if you want you can pass the method you want to call via parameter as of type callback.
If you change the entire class to be an instance class (deleting all the static keywords) then you can use $this inside the anonymous function to call any method of the class you are in.
From the PHP manual:
Closures may also inherit variables from the parent scope.
As specified:
In version 5.4.0 $this can be used in anonymous functions.
class MyClass {
public function MyFunction($mylocalparam){
MyStaticClass:MyStaticMethod(function($myparam) use($mylocalparam){
$this->MyFunction2($mylocalparam);
});
}
private function MyFunction2($someobject){
}
}

Translate class function to lambda function

I'm trying to pull of something like this:
class helper {
.. some class variables ..
public function somehelper ($somevar)
{
.. some action ..
}
}
to - $somehelper('somevar!')
i want it to be a lambda function without me declaring a new lambda function. I want the code to be created automatically. I want to create this lambda function autamatically via a core class that will create to each helper function - lambda function with the functions name. I dont want to specify it. I have a template file included, and an instance of a class helper. <a href..><?= $makeSeo($url); ?></a> this will lead to - public function makeSeo($url) in a helper class. autmatically.
Thanks in advanced!
Sounds like you want something to create a lambda given a method name? Assuming your 'Helper' class methods are static, something like this would do it
function makeHelper($fn)
{
//note use of PHP5.3 closure syntax
$helper = function($param) use ($fn) {
return Helper::$fn($param);
};
return $helper;
}
//create a new lambda
$foo=makeHelper('somehelper');
//call it...
$foo('Hello world);
I think you're saying that you want the variable $somehelper to call helper::somehelper from outside the context of the class.
$somehelper = function($somevar) {
return helper::somehelper($somevar);
});
This presume that the somehelper is intended to be a static function (i.e. should be marked as public static function. If it is not, you're going to have to provide more information about where the class instance comes from.

PHP - calling class that implements interface without knowing class name

I'm trying to create a PHP file that calls a function in another file. Some sample code:
Interface code:
interface AJAXDispatcher {
static function dispatch($action);
}
Implementation:
class myAJAX implements AJAXDispatcher {
static function dispatch($action) {
if ($action === "action1") {
do_something();
}
This seems ok to me. I try to call it by first importing the file that it's in. I'm trying to make it independent of the name of the class so that I can do something like this:
AJAXDispatcher::dispatch($action);
Thought this would work as myAJAX would inherit from AJAXDispatcher, but I get the following error:
Fatal error: Cannot call abstract method AJAXDispatcher::dispatch() in ....
Anyone know what I'm doing wrong?
Interfaces with static methods don't make any sense, because to call a static method you (usually) need to know the class name.
Instead, you should make the method non-static and create an instance of myAJAX somewhere. The code that calls the AJAXDispatcher receives the instance and calls it. You can use type hinting to ensure you are getting the right instance.
interface AJAXDispatcher {
public function dispatch($action);
}
class myAJAX implements AJAXDispatcher {
public function dispatch($action) {
do_something();
}
}
class Controller {
private $dispatcher;
public function __construct(AJAXDispatcher $dispatcher) {
$this->dispatcher = $dispatcher;
}
public function action($action) {
$this->dispatcher->dispatch($action);
}
}
$dispatcher = new myAJAX();
$controller = new Controller($dispatcher);
$controller->action('index');
This example uses the Dependency Injection design pattern.
An interface has no method implementation. It only defines a public API that classes have to implement. How they implement it, is up to the concrete classes. Thus, you cannot call methods of an interface directly, because there is no code to call. You have to call the method on the implementing class.
Have a look at the PHP Manual on Interfaces.
No, you can't do that. There are several things wrong here
That's now how inheritance works. Method chaining goes up the class hierarchy, now down.
Static methods are connected to the class. If you override a static method in a subclass, and want to invoke the subclass' version, you must reference the subclass explicitly.
Interfaces have no implementation. Even if the language allowed the type of call you're making, nothing would happen. AJAXDispatcher::dispatch() has no body.
You're going to have to re-think your strategy here.

Categories