How to call a method using array without object? - php

How can I call a method like in laravel or slim route?
Let's say I have class like this:
namespace App;
class App
{
public function getApp(){
return "App";
}
}
and I want to call on this way
$route->get('App\App','getApp');
How can I do this?

The simplest way
call_user_func_array(['App\App', 'getApp'], $params_if_needed);
php.net source call_user_func_array()
If you need to check if method exists, just use
method_exists('SomeClass','someMethod') // returns boolean
php.net method_exists()
So you Router class may be next:
class Router
{
public function get($class, $method)
{
if($_SERVER['REQUEST_METHOD'] !== 'GET') {
throw new SomeCustomNotFoundException();
}
if (!method_exists($class, $method)) {
throw new SomeCustomNoMethodFoundException();
}
call_user_func_array([$class, $method], $_REQUEST); //with params
// OR
call_user_func([$class, $method]); //without params, not sure
}
}
If you want to do in the more clever way, you can use Reflection, it will give you information about class/method existence and also give information about method params, and which of them are required or optional.
UPDATE: This example expects methods to be static. For non-static you can add check, in the Router class, for class existence (class_exists($class)) and do smth like this
$obj = new $class();
$obj->$method(); //For methods without params
UPDATE(2) to check this out go here and paste next code
<?php
class Router
{
public function get($class, $method)
{
if($_SERVER['REQUEST_METHOD'] !== 'GET') {
throw new SomeCustomNotFoundException();
}
if(!class_exists($class)) {
throw new ClassNotFoundException();
}
if (!method_exists($class, $method)) {
throw new SomeCustomNoMethodFoundException();
}
call_user_func_array([$class, $method], $_REQUEST); //with params
// OR
//call_user_func([$class, $method]); //without params, not sure
}
}
class Test
{
public static function hello()
{
die("Hello World");
}
}
$route = new Router();
$route->get('Test', 'hello');

Related

Can't call parent function inside __call() in Laravel

This is my code below.
class MyModel extends Model
{
public function __call($method, $parameters = null) {
if($method == 'create'){
return parent::create($parameters[0]);
if(!$created) {
throw new \App\Exceptions\EloquentException;
}else{
return $created;
}
}
}
}
The problem is that when I call update function of MyModel class instance from postman, something bad happens. It gets stuck and I have to restart my computer each time. So what may the problem be?
I'll try to assume you're simply trying to have a common handler for the create function, that is, to throw an EloquentException in case the create returns a null or false.
If that's the case, you have an excess return statement on line above the if statement, and you should assign the return value of the parent's create method to a variable $created that you use later. You may also remove the else part as code below throw is never going to be executed if the exception is thrown.
class MyModel extends Model
{
public function __call($method, $parameters = null)
{
if ($method == 'create') {
$create = parent::create($parameters[0]);
if (!$created) {
throw new \App\Exceptions\EloquentException;
}
return $created;
}
}
}
It would be better if you could elaborate on the task you're trying to achieve, I feel you're doing it the wrong way.

Magic function for auto-invoke in php

In my Class I have methods with the same prefix at the beginning.
sendMessage()
sendPhoto()
sendDocument()
So what I need, is just somehow initialize another method in the class every time when these methods (with prefix) are initialized without putting anything in methods' body.
Is there any way to do this out of the box? Some magic php function which triggers every time when method with prefix is invoked...
Use __call() magic method:
class MagickClass {
public function __call($name, params = []) {
if (strpos($name, 'send') === 0) {
$this->process(strtolower(str_replace('send', '', $name)));
} else {
throw new Exception('Not found', 404);
}
}
private function process($action) {
}
}

Create Class object from variable value in Laravel

I want to create an object of a class from a returned string but I am getting error Class **test_report** not found. My code:
public function display_report_builder($report_name = null)
{
$column_listing = new $report_name;// gets the test_report
return view('column_list')->with(['column_list_names' => $column_listing->columns]);
}
This isn't the better approach here. What you should do is to use a Factory design pattern:
class ReportFactory
{
public static function create($report_name)
{
switch($report_name) {
case 'test_report': return new TestReport();
default: throw new Exception('report not found');
}
}
}
Then you call with $column_listing = ReportFactory::create($report_name);
Why? Because you avoid "magic variables" with unknown data; you can trace errors properly; you can use namespace; you can extend functionalities easily, and easily activate or deactivate objects (or reports in this case); you have a cleaner code, and so on...
test if the class name (string) really is a valid class :
public function display_report_builder($report_name = null)
{
$column_list_names = null;
if (class_exists($report_name) && is_a($report_name, App\reports\test_report::class, true)) {
$column_listing = new $report_name;
$column_list_names = $column_listing->columns;
}
return view('column_list', compact('column_list_names'));
}
is_a() : Checks if the given object is of this class or has this class
as one of its parents.

Injecting single-use object into class

I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.

how to pass a parameter to method with php's is_callable

I have to create a variable that is callable with php's is_callable
I have done this:
$callable = array(new MyClass, 'methodName');
But I want to pass a parameter to the method.
How can I do that?
Cause using symfony's event dispatcher component will be like:
$sfEventDispatcher->connect('log.write', array(new IC_Log('logfile.txt'), 'write'));
The first parameter is just a event name, the second is the callable variable.
But I can only call the write method, I want to pass a parameter to it.
Since PHP 5.3 you can use anonymous functions.
You should connect the listener like this:
$dispatcher->connect('my.event', function($event) {
$my_class = new MyClass;
$my_class->myMethod($event, array('my_param' => 'my_value'));
});
Then you will be able to get the parameters array in the listener:
class MyClass {
public function myMethod(sfEvent $event, $parameters) {
$my_value = $parameters['my_param'];
$event_param_value = $event['event_param'];
}
}
Now you can notify the event normally:
$dispatcher->notify(new sfEvent($this, 'my.event', array('event_param' => 'event_param_value')));
Take care that this listener can't be disconnected.
If you need to disconnect it, put the anonymous function in a variable:
$dispatcher->connect('my.event', $my_listener = function($event) {
$my_class = new MyClass;
$my_class->myMethod($event, array('my_param' => 'my_value'));
});
You should be able to disconnect with:
$dispatcher->disconnect('my.event', $my_listener);
You don't pass parameters to your listener callbacks (without extending the core). Symfony will be the one calling it and will pass an event object. If you need additional info, you can create a different method that calls another method where you can control the parameters.
$callable1 = array(new MyWriter, 'write1');
$callable2 = array(new MyWriter, 'write2'); // or $callable2 = array($callable1[0], 'write2');
$sfEventDispatcher->connect('log.write', $callable1);
$sfEventDispatcher->connect('log.write', $callable2);
And your callback class methods can be something like:
class MyWriter
{
public function write($event, $num)
{
// do something
}
public function write1($event)
{
$this->write($event, 1);
}
public function write2($event)
{
$this->write($event, 2);
}
}
Alternatively, you can create properties that act as state that your write function can check:
class MyWriter
{
public $state = 1;
public function write($event)
{
if ($this->state == 1) {
// do this
} else {
// do this instead
}
}
}
This is trickier as you'd have to set state before a pertinent event is triggered which may not prove feasable, depending on the specifics of your situation:
$callable[0]->state = 2;

Categories