Inline interface implementation - Implement interface methods at declaration - php

I come from java, where we can do something like this:
Action.java:
public interface Action {
public void performAction();
}
MainClass.java:
public class MainClass {
public static void main(String[] args) { //program entry point
Action action = new Action() {
public void performAction() {
// custom implementation of the performAction method
}
};
action.performAction(); //will execute the implemented method
}
}
As you can see, I'm not creating a class which implements Action, but I'm implementing the interface directly on declaration.
Is something like this even possible with PHP?
What I've tried:
action.php:
<?php
interface Action {
public function performAction();
}
?>
myactions.php:
include "action.php";
$action = new Action() {
public function performAction() {
//do some stuff
}
};
What I get:
Parse error: syntax error, unexpected '{' in myactions.php on line 3
So, my question is: is something like this possible with PHP? How should I do it?

With PHP 7, this has become possible with anonymous classes.
$action = new class implements Action() {
public function performAction() {
//do some stuff
}
};

No, can't. PHP doesn't offer anonymous classes like Java does. You can however try to simulate the behaviour you want, but the results will be...mixed at best.
Here's some code:
interface Action
{
public function performAction();
}
class MyClass
{
public function methodOne($object)
{
$object->performAction(); // can't call directly - fatal error
// work around
$closure = $object->performAction;
$closure();
}
public function methodTwo(Action $object)
{
$object->performAction();
}
}
$action = new stdClass();
$action->performAction = function() {
echo 'Hello';
};
$test = new MyClass();
$test->methodOne($action); // will work
$test->methodTwo($action); // fatal error - parameter fails type hinting
var_dump(method_exists($action, 'performAction')); // false
var_dump(is_callable(array($action, 'performAction'))); // false
Hope it helps!

Related

PHP 8 migration Fatal error: Declaration must be compatible

We are moving forward to PHP 8 from PHP 7.4, we are facing Declaration must be compatible Fatal error in our code to custom parameter type, we need a proper solution with less code changes.
Kindly refer below code snippet
ERROR
Fatal error: Declaration of ClientReactieView::getUrlAddData(?ClientReactie $clientReactie = null) must be compatible with Overview\BaseView::getUrlAddData(?Storm\Model $model = null) in /var/www/html/system/tmp/class_client_reactie_view.php on line 102
abstract class file : abstract_class_base_view.php
<?php
namespace Overview;
use Storm;
abstract class BaseView {
public static function getUrlAddData(Storm\Model $model = null){
// ...
return $urlAddData;
}
}
Child class file : class_client_reactie_view.php
<?php
class ClientReactieView extends Overview\BaseView {
public static function getUrlAddData(ClientReactie $clientReactie = null){
// ...
return $urlAddData;
}
}
Custom parameter type class_client_reactie.php
class ClientReactie extends Storm\Model {
// ...
}
Our application is already developed & working fine with PHP 7.4, We require solution to resolve this fatal error with less code changes
The error is telling your code is illogical.
The definition of BaseView makes a promise: you can call getUrlAddData and pass any instance of Storm\Model, or null.
The definition of ClientReactieView says that it extends BaseView, so inherits that promise. But then it changes that promise: you can call getUrlAddData, but you're not allowed to pass anything that's not a ClientReactie.
This is "covariance of input", and has always been forbidden for non-static methods - if $foo instanceof BaseView is true, then $foo->getUrlAddData(...) would have to accept all values that the definition in BaseView allowed.
What's new in PHP 8 is that this is enforced for static methods as well, so that the same guarantee applies to "late static binding" calls such as this:
abstract class BaseView {
public static function getUrlAddData(Storm\Model $model = null){
// ...
return $urlAddData;
}
public static function doSomethingElse(Storm\Model $model = null){
$urlAddData = static::getUrlAddData($model);
// ...
}
}
class ClientReactieView extends Overview\BaseView {
public static function getUrlAddData(ClientReactie $clientReactie = null){
// ...
return $urlAddData;
}
}
ClientReactieView::doSomethingElse(new Storm\Model);
// ERROR! The call goes to BaseView::doSomethingElse, which accepts any Model
// But static::getUrlAddData resolves to ClientReactieView::getUrlAddData
// and that has an incompatible signature
So the correct fix is to honour the promise made by the parent class:
class ClientReactieView extends Overview\BaseView {
public static function getUrlAddData(Storm\Model $model = null){
if ( ! $model instanceof ClientReactie ) {
// Figure out what to do with such calls
}
// ...
return $urlAddData;
}
}

implement function as object in PHP

Is it possible to implement functions as object instances in PHP? I'm used to doing this in Java with the code below, but I can't seem to find a similar approach in PHP. Is it possible?
interface OneVarFunction {
public int eval(int x);
}
static void routine() {
OneVarFunction func1 = new OneVarFunction() {
public Boolean eval(int x) {
return x*x;
}
};
OneVarFunction func2 = new OneVarFunction() {
public Boolean eval(int x) {
return 2*x+1;
}
};
}
It is possible in PHP7+
in your case, an example would be
interface OneVarFunction {
public function eval($x);
}
//...
$test = new class implements OneVarFunction {
public function eval($x) {
return $x*$x;
}
};
For more information, see the corresponding site of the PHP Manual, which also provides further examples.
It is not possible in versions prior to PHP7, though.

Assign functions from another file to a Class

I am trying to add functions to class from a separate file, I wonder if this could be possible!
$mClass = new MyClass();
$mClass->new_Functions[0](10); // Is there a way to have it in this form?
class myClass
{
private $Pvar = 5;
$new_Fcuntions;
function __construct()
{
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
}
}
[additional.functions.php] file
function operate($y)
{
return $this->Pvar * $y;
}
----- Edited ------- as it wasn't clear!
"additional.functions.php" is a module and there will be multiple modules to be added to the application, and every module could have more than single function and modules could call one another!
additional.functions.php [module file]
function operate($y)
{
return $this->Pvar * $y;
}
function do-more($foo)
{
return $this->operate(20) + $foo;
}
another.functions.php [another module]
function do-another($foo)
{
return $this->do-more(30) - $foo;
}
function add($foo, $bar)
{
return $foo + $bar;
}
appreciate every participation, its been a while since I am trying to maneuver around with it!
Is this possible or should I give up!
It looks to me like you are looking for Traits, which are a new feature as of PHP 5.4.0. Using traits, you can have snippets of code "mixed in" to other classes, a concept known as "horizontal reuse".
If you are not looking for traits, it's possible that you could do what you wanted with Runkit, however I would suggest staying as far away from it as possible, if you are not genuinely interested in PHP internals as well.
In any event, whatever you are trying to do is very interesting
I got it to work with dependency injection. The pvar has to be public or create a __get method to return the private variable. I also used the function name because it seems cleaner to me to use it via name rather than it's position in the list but if you want to keep that then just put $key where you see $value from the line: $this->function_list[$value] = ...
function operate($y, $that)
{
return $that->Pvar * $y;
}
class Example {
public $function_list = array();
private $Pvar = 5;
public function __construct()
{
$list = get_defined_functions();
$that = $this;
foreach ($list['user'] as $key => $value) {
$this->function_list[$value] = function() use ($value, $that) {
print call_user_func_array($value, array_merge(func_get_args(), array($that )));
};
}
}
public function __get($key)
{
if (isSet($this->$key)) {
return $this->$key;
} else {
throw new \Exception('Key "'.$key.'" does not exist');
}
}
}
$Ex = new Example();
$Ex->function_list['operate'](10);
If you want to extend MyClass from your modules (and not to initialize it, like in your example code), than you could do it in a way like this:
<?php
namespace modules\MyModuleA;
class MyClassExtension
{
private $MyObject;
public function __construct(\MyClass $MyObject)
{
$this->MyObject = $MyObject;
}
public function doSomething($anyParameter)
{
return $this->MyObject->doSomethingElse($anyParameter * 5, 42, 'foo');
}
}
And MyClass:
<?php
class MyClass extends \Extensible
{
// some code
}
abstract class Extensible
{
private $extensions = [];
public function extend($extension)
{
$this->extensions[] = $extension;
}
public function __call($methodName, $parameters)
{
foreach ($this->extensions as $Extension) {
if (in_array($methodName, get_class_methods($Extension))
return call_user_func_array([$Extension, $methodName], $parameters);
}
throw new \Exception('Call to undefined method ' . $methodName . '...');
}
public function hasExtension($extensionName)
{
return in_array($this->extensions, $extensionName);
}
}
And put it all together:
<?php
$moduleNames = ['MyModuleA', 'MyModuleB'];
$MyObject = new \MyClass;
foreach ($moduleNames as $moduleName) {
$className = '\\modules\\' . $moduleName . '\\MyClassExtension';
$module = new $className($MyObject);
$MyObject->extend($module);
}
// Now you can call a method, that has been added by MyModuleA:
$MyObject->doSomething(10);
You should add an interface for the extension classes of course...
The problem is: What happens if any code in your application calls a method of $MyObject, that is not there, because the module has not been loaded. You would always have to check if ($MyObject->hasExtension('ModuleA')) { ... }, but, of course, the application shouldn't be aware of any module. So I would not design an application in such a way.
I would suggest to use traits (mix-ins). See PHP reference
If you can have another class in that file instead of file with functions
- the best solution will be Traits
http://php.net/manual/en/language.oop5.traits.php
or using inheritance
If you move that code to class you can avoid a lot of unnecessary code. I mean:
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
It'll be e.g.:
class myClass extends MyBaseClassWithMyAwesomeFunctions
{
private $Pvar = 5;
}
Maybe this approach helps you:
In the files with the additional functions, don't define named functions, but return a closure, that expects (at least) the object (instance of MyClass) as parameter:
<?php
// additional.functions.php
return function ($myObject) {
$Object->multiplyPvar($myObject->getTheNumber());
$Object->doSomethingElse(42, 'foo');
};
The client, that builds MyClass collects those functions from the files into the array:
<?php
$files = [
'/path/to/my/additional.functions1.php',
'/path/to/my/additional.functions2.php'
];
$initFunctions = [];
foreach ($files as $path)
$initFunctions[] = include $path;
$MyObject = new \MyClass($initFunctions);
The constructor then calls those functions:
<?php
class MyClass
{
public function __construct(array $additionalInitFunctions)
{
foreach ($additionalInitFunctions as $additionalInitFunction)
$additionalInitializerFunction($this); // you can also add parameters of course
}
}
This way the class keeps very well testable as well as the function files. Maybe this could help you in any way. You should never ever think about modifying the internal (private) state of an object directly from any code from outside of the class. This is not testable! Think about writing tests before you implement your code (called "test driven development"). You will see, it is not possible to test a class, if you allow any code outside of that class to modify the internal (private) state of the class instance. And you don't want to have this. If you change some internal implementation detail in your class without breaking the unit test of that class, you will anyways probably break some code in any of your additional.functions.php files and no test will tell you: "Hey: you've broken something right now".

Create and use anonymous object in PHP

Say I have a simple class and I create it and call a function on it like this:
class tst
{
private $s = "";
public function __construct( $s )
{
$this->s = $s;
}
public function show()
{
return $this->s;
}
}
$t = new tst( "hello world" );
echo "showing " . $t->show() . "\n";
Is there any syntax or workaround that will allow me to instantiate an instance of tst and call the show() function without assigning the object to a variable? I want to do something like:
echo new tst( "again" )->show();
I don't want to declare my functions as static as I want to use them in both of the above examples.
You can't do what you want exactly, but there are workarounds without making things static.
You can make a function that returns the new object
function tst( $s ) {
return new tst( $s );
}
echo tst( "again" )->show();
To answer your question:
public static function create( $s )
{
return new tst($s);
}
public function show()
{
return $this->s;
}
The above will allow you to do tst::create("again")->show(). You can rename create as you like.
Agile Toolkit uses this approach everywhere. It uses add() method wrapper which is defined for global object ancestor. Here is some real-life code:
$page
->add('CRUD')
->setModel('User')
->setMasterField('admin',false);
This code creates 'CRUD' view, puts it on the page, creates and links with Model_User class instance which receives additional condition and default value for boolean 'admin' field.
It will display a CRUD control on the page with add/edit/delete allowing to edit all users except admins.
Here is code to describe concept:
class AbstractObject {
public $owner;
function add($class){
$c=new $class;
$c->owner=$this;
return $c;
}
}
class Form extends AbstractObject {
function dosomething(){
return $this;
}
}
class OtherForm extends Form {}
$object->add('Form')->dosomething()->owner
->add('OtherForm'); // etc
I think it's awesome and very practical approach.
p.s. I have to note new syntax for exceptions:
throw $this->exception('Something went bad');
using $this links exception to the object, which is at fault, which also can set default class for exception.

PHP: How to realize event handler?

I want to add custom event handler to object's method.
I've got a class with method.
class Post {
public function Add($title) {
// beforeAdd event should be called here
echo 'Post "' . $title . '" added.';
return;
}
}
I want to add an event to method Add and pass method's argument(s) to the event handler.
function AddEventHandler($event, $handler){
// What should this function do?
}
$handler = function($title){
return strtoupper($title);
}
AddEventHandler('beforeAdd', $handler);
Is it possible to do something like this? Hope my question is clear.
Should be pretty easy using the functions defined here http://www.php.net/manual/en/book.funchand.php
In particular you should keep an handler array (or array of arrays if you want multiple handlers for the same event) and then just do something like
function AddEventHandler($event, $handler){
$handlerArray[$event] = $handler;
}
or
function AddEventHandler($event, $handler){
$handlerArray[$event][] = $handler;
}
in case of multiple handlers.
Invoking the handlers then would be just matter of calling "call_user_func" (eventually in a cycle if multiple handlers are needed)
Well, if you are using < php 5.3 then you cannot create a closure in such a way, but you can come close with create_function(); This would be
$handler = create_function('$title', 'return strtoupper($title);');
Then you store $handler in the class and you can call it as you desire.
Methods
You have multiple methods how to do it described by ircmaxell here.
And here is ToroHook used in ToroPHP (Routing lib).
Hook
class ToroHook {
private static $instance;
private $hooks = array();
private function __construct() {}
private function __clone() {}
public static function add($hook_name, $fn){
$instance = self::get_instance();
$instance->hooks[$hook_name][] = $fn;
}
public static function fire($hook_name, $params = null){
$instance = self::get_instance();
if (isset($instance->hooks[$hook_name])) {
foreach ($instance->hooks[$hook_name] as $fn) {
call_user_func_array($fn, array(&$params));
}
}
}
public static function remove($hook_name){
$instance = self::get_instance();
unset($instance->hooks[$hook_name]);
var_dump($instance->hooks);
}
public static function get_instance(){
if (empty(self::$instance)) {
self::$instance = new Hook();
}
return self::$instance;
}
}
Using hook
It is simple call it like this:
ToroHook::add('404', function($errorpage){
render("page/not_found", array("errorpage" => $errorpage));
});
Take look on my sphido/events library:
it's easy to use (few lines of code)
based on PHP Function handling
allow prioritising listeners
add/remove listeners
filter values by functions
stop propagation in function chain
add default handler
Event handler example
on('event', function () {
echo "wow it's works yeah!";
});
fire('event'); // print wow it's works yeah!
Filter function example
add_filter('price', function($price) {
return (int)$price . ' USD';
});
echo filter('price', 100); // print 100 USD

Categories