Call to Object method, executing a closure Property - Error - php

I have 2 classes, an event which holds the associated sessions the listener will check for and the method to execute and also a listener which monitors the sessions on page load and executes the corresponding event to that.
class Event
{
private $Sesh;
private $Method;
public function set(
$sesh = array(),
$method
) {
$this->Sesh = $sesh;
$this->Method = $method;
}
public function get(
) {
return [$this->Sesh,$this->Method];
}
}
class Listener
{
private $Sesh;
public function setSesh(
$foo
) {
$this->Sesh = $foo;
}
private $Event;
public function set(
$foo,
Event $event
) {
$this->Event[$foo] = $event;
}
public function dispatch(
$foo
) {
$state = true;
if(isset($this->Event[$foo]))
{
foreach($this->Event[$foo]->get()[0] as $sesh)
{
if(!isset($this->Sesh[$sesh]) || empty($this->Sesh[$sesh]))
{
$state = false;
}
}
}
// this line seems to be the cause at the method closure execution - not sure why?
return ($state) ? [true,$this->Event->get()[1]()] : [false,"Event was not triggered."];
}
}
When I run something like this:
$e = new Event;
$e->set(['misc'], function() { return 'Event method called.'; });
$l = new Listener;
$l->setSesh(array('misc' => 'example'));
$l->set('example', $e);
$l->dispatch('example');
I am getting this error:
FATAL ERROR Uncaught Error: Call to a member function get() on array in /home/phptester.net/public_html/code.php70(5) : eval()'d code:55 Stack trace: #0 /home/phptester.net/public_html/code.php70(5) : eval()'d code(65): Listener->dispatch('example') #1 /home/phptester.net/public_html/code.php70(5): eval() #2 {main} thrown on line number 55
Which seems to correspond to the method execution in the dispatch method of the Listener object, although I don't see why it is doing this? I am running PHP 7.0
My expected output in this case would be a returned array from the dispatch method containing: true and a string - 'Event method called.'
EDIT:
// returns array(1) { [0]=> string(4) "misc" }
var_dump($this->Event[$foo]->get()[0]);
// returns object(Closure)#2 (0) { }
var_dump($this->Event[$foo]->get()[1]);
// returns expected string output
var_dump($this->Event[$foo]->get()[1]());

Related

Using call_user_func on Class Method

I am trying to setup a PHP method that gets passed an object and its method name, then gets calls that method of the object. I am trying to limit the usage of strings in my code, so I would like to do it using a custom enum. When I run this example though, this is the output I receive:
getTest
WARNING call_user_func() expects parameter 1 to be a valid callback, second array member is not a valid method on line number 43
echo call_user_func(array($object, $method));
Although it seems to print out the correct method, it says the method being passed isn't a valid method. I'm confused because I followed the tutorial on PHP.net
http://php.net/manual/en/function.call-user-func.php
What is the proper way to use call_user_func on a class's method? Please let me what I am missing/doing wrong?
abstract class MyEnum
{
final public function __construct($value)
{
$c = new ReflectionClass($this);
if(!in_array($value, $c->getConstants())) {
throw IllegalArgumentException();
}
$this->value = $value;
}
final public function __toString()
{
return $this->value;
}
}
class one {
private $test = 1;
public function getTest() {
return $this->test;
}
}
class two {
private $quiz = 2;
public function getQuiz() {
return $this->quiz;
}
}
class Number extends MyEnum {
const ONE = "getTest";
const TWO = "getQuiz";
}
function testCallback($object, $method) {
echo $method;
echo call_user_func(array($object, $method));
}
$temp1 = new one();
$temp2 = new two();
testCallback($temp1, new Number(Number::ONE));
This is how I would do it, as I said in the comments you can call it directly without invoking an additional function call
function testCallback($object, $method) {
echo $method;
if( !method_exists( $object, $method) ) throw new Exception( "Method does not exist ".$object::class.'::'.$method);
echo $object->$method();
}
The only time to use call_user_func* is when you have arguments ( even then you could use reflection ) Such as this.
function testCallback($object, $method, array $args = []) {
echo $method;
if( !method_exists( $object, $method) ) throw new Exception( "Method does not exist ".$object::class.'::'.$method);
echo call_user_func_array( [$object,$method], $args);
}
Or with reflection ( probably tiny bit slower )
function testCallback($object, $method, array $args = []) {
echo $method;
if( !method_exists( $object, $method) ) throw new Exception( "Method does not exist ".$object::class.'::'.$method);
echo (new ReflectionMethod( $object, $method))->invokeArgs( $object, $args );
}

Run method chain from a string value

I have a question about getting value from running a method from a string. While I am able to handle a single method call from a string I am curious how to call a chain of methods from a string.
For example. $project is an Object.
$method1 = "name";
$project->$method1; // It shows the valid results
$method2 = "get()->first()->name";
$project->get()->first()-name; // It shows the valid results
$project->$method2; // get a null result
Please help to find a way to make the $method2 work. And what happen if I have params inside those methods?
The reason here is I have made an array of customized methods. It can be run line by line, but I am thinking of a way to turn them into a loop, so it's more efficient. Put the methods in to a file then get values by looping to them.
Array = ["getvalue1()", "getvalue2()",...."getValuen()->anotherMethod()->value"]
Thanks,
If you want nested try something like this:
private function callMethodChain($model, $methodChain)
{
return array_reduce(explode('->', $methodChain), function($model, $method) {
return $model->$method;
}, $model);
}
This will go through a chain of method calls as your described. If some of the chain (the last piece) is a property I think I once rigged up the following to handle it:
protected function callMethodChain($model, $methodChain)
{
return array_reduce(explode('->', $methodChain), function($model, $method) {
try {
return $model->$method;
} catch (Exception $e) {
return $model->$method();
}
}, $model);
}
If you want to add params try replacing $model->method with:
call_user_func_array(
array($project, 'your_method'),
$params
);
Try this approach:
$method1 = 'name';
$project->{$method1}();
Run method from a string value
Use call_user_func() and call_user_func_array().
call_user_func_array() suits good if you are passing parameters
call_user_func_array(
array($project, 'your_method'),
$params
);
Chain function
function chain_fun($chain,$object)
{
return array_reduce(explode('->', $chain), function ($obj, $method) {
if(preg_match('/[()]/',$method)){
$method=trim($method,'()');
return $obj->$method();
}
return $obj->$method;
}, $object);
}
Here is test
akshay#db-3325:/tmp$ cat test.php
<?php
class Testclass
{
private $str;
function __construct()
{
$this->str = new StdClass;
}
function addA()
{
$this->str->a='A';
return $this;
}
function addB()
{
$this->str->b='B';
return $this;
}
function get()
{
return $this->str;
}
}
function chain_fun($chain,$object)
{
return array_reduce(explode('->', $chain), function ($obj, $method) {
if(preg_match('/[()]/',$method)){
$method=trim($method,'()');
return $obj->$method();
}
return $obj->$method;
}, $object);
}
$object = new Testclass();
// Output 1
print_r(chain_fun("addA()->addB()->get()", $object));
// Output 2
echo chain_fun("addA()->addB()->get()->a", $object);
?>
Output
akshay#db-3325:/tmp$ php test.php
stdClass Object
(
[a] => A
[b] => B
)
A

I couldn't acces variable in child class

First, Thanks a lot
My problem is that i have Page which (indexStatic.php) have content variable return and affect some head tags like include js files but when i start file is give this message:
I want to access variables which are displayControler.php in indexStatic.php
<b>Fatal error</b>: Uncaught exception 'Exception' with message 'js doesn't exist in this class' in C:\webServer\htdocs\blabla\admin\AdminPanel\core\modelAbstract.php:25
Stack trace:
#0 C:\webServer\htdocs\blabla\admin\AdminPanel\static\indexStatic.php(2): ModelAbstract->__set('js', '<script src="re...')
#1 C:\webServer\htdocs\blabla\admin\AdminPanel\core\tools.php(14): include('C:\webServer\ht...')
#2 C:\webServer\htdocs\blabla\admin\AdminPanel\core\displayControler.php(33): tools->includeFile('/static/indexSt...')
#3 C:\webServer\htdocs\blabla\admin\AdminPanel\core\displayControler.php(28): displayControler->getContentHtml('/static/indexSt...')
#4 C:\webServer\htdocs\blabla\admin\AdminPanel\core\displayControler.php(18): displayControler->setUrlTofunc(NULL)
#5 C:\webServer\htdocs\blabla\admin\AdminPanel\index.php(17): displayControler->showHtml()
#6 {main}
thrown in <b>C:\webServer\htdocs\blabla\admin\AdminPanel\core\modelAbstract.php</b> on line <b>25</b><br />
indexStatic.php is included in displayControler.php lets see files :
indexStatic.php File :
<?php
$this->js ='<script src="resources/scripts/editablegrid-2.0.1.js"></script> ';
$this->js .='<script src="resources/scripts/jquery-1.7.2.min.js" ></script>';
$this->js.='<script src="resources/scripts/demo.js" ></script>';
return'<div class="content-box"><!-- Start Content Box -->
<script type="text/javascript">
window.onload = function() {
datagrid = new DatabaseGrid();
};
</script>
</div>';
?>
displayControler.php
class displayControler extends ModelAbstract
{
protected $js;
protected $tools;
protected $title;
protected $meta;
protected $content='';
protected $urls=array(
'main'=>'static/aboutStatic.php'
);
public function __construct(){
$this->setTitle(defaultVar::$static['title']);
}
public function showHtml(){
$this->setUrlTofunc(#$_GET['cont']);
include 'static/adminStatic.php';
}
public function setUrlTofunc($url){
$this->tools=new tools();
if(isset($url) and isset($this->urls[$url]) ){
$this->getContentHtml($this->urls[$url]);
}else{
$this->getContentHtml(defaultVar::$static['filePath']);
}
}
public function getContentHtml($url){
$catch = $this->tools->includeFile($url);
$this->setContent($catch);
}
}
and modelAbstract.php is have :
abstract class ModelAbstract{
public function __call($name, $args){
$methodPrefix = substr($name, 0,3);
$methodProperty = strtolower($name[3]).substr($name,4);
switch ($methodPrefix){
case "set":
if (count($args) == 1)
$this->__set($methodProperty,$args[0]);
else
throw new \Exception("magic method only supports one argument");
break;
case "get":
return $this->__get($methodProperty);
break;
default:
throw new Exception("magic methods only supported for get and set");
}
}
public function __set($name,$value){
if (property_exists($this, $name))
$this->$name = $value;
else
throw new \Exception("$name doesn't exist in this class");
}
public function __get($name){
if (property_exists($this, $name))
return $this->$name;
else
throw new \Exception("$name doesn't exist in this class");
}
}
The $js property is protected so you can not access it directly. You have magics in the Model class which make the getter and setter for your properties.
This means it will go through the magic call. Try using $this->setJs("") instead of $this->js = "".

Parent Class don't catch exceptions from overwritten method in child class

I have this problem with Slim framework. I have class Template that have render method and I want for Slim to render objects of this class if they are returned by route handlers
$app->get('/test', function() {
return new Template('main', function() use ($error) {
return array(
'content' => "Hello"
);
});
});
it work I created child class (in System.php)
class System extends Slim {
function __constructor() {
Slim::__construct();
}
private function auto_render_fun($callable) {
$app = $this;
return function() use ($callable, $app) {
$args = func_get_args();
$ret = call_user_func_array($callable, $args);
if ($ret instanceof Template) {
//render Template - Slim ignore return value
$app->response()->body($ret->render());
}
};
}
protected function mapRoute($args) {
$last = count($args)-1;
$args[$last] = $this->auto_render_fun($args[$last]);
return Slim::mapRoute($args);
}
}
I wanted to do the same thing with notFound
$app->notFound(function () use ($app) {
$response = $app->response();
$response['Content-Type'] = 'text/html';
return new Template('main', function() {
return array('content' => new Template('error_404', null));
});
});
so I've overwritten notFound function to wrap Closure and render it's return value
First I try to use smaller code
public function notFound($callable = null) {
if (!is_null(($callable))) {
$this->router->notFound($this->auto_render_fun($callable));
} else {
Slim::notFound();
}
}
I also try this (copy and modify old code).
public function notFound($callable = null) {
if ( !is_null($callable) ) {
$this->router->notFound($this->auto_render_fun($callable));
// $this->router->notFound($callable); // old line
} else {
ob_start();
$customNotFoundHandler = $this->router->notFound();
if ( is_callable($customNotFoundHandler) ) {
call_user_func($customNotFoundHandler);
} else {
call_user_func(array($this, 'defaultNotFound'));
}
$this->halt(404, ob_get_clean());
}
}
but the reason why it didn't work is that it trow Slim_Exception_Stop that's suppose to be cached by Slim here is that line for code that call $this->notFound();
https://github.com/codeguy/Slim/blob/master/Slim/Slim.php#L1160
it's inside try..catch.
Here is stack trace (I cached it inside notFound function - but it should be handled in Slim class).
Slim_Exception_Stop in file libs/Slim/Slim/Slim.php at 862
0: Slim->stop() in libs/Slim/Slim/Slim.php at 882
1: Slim->halt(integer, string) in libs/System.php at 187
2: System->notFound() in libs/Slim/Slim/Slim.php at 1161
3: Slim->call() in libs/Slim/Slim/Middleware/Flash.php at 84
4: Slim_Middleware_Flash->call() in libs/Slim/Slim/Middleware/MethodOverride.php at 91
5: Slim_Middleware_MethodOverride->call() in libs/Slim/Slim/Middleware/PrettyExceptions.php at 65
6: Slim_Middleware_PrettyExceptions->call() in libs/Slim/Slim/Middleware/PrettyExceptions.php at 65
7: Slim_Middleware_PrettyExceptions->call() in libs/Slim/Slim/Slim.php at 1098
8: Slim->run() in index.php at 573
Ok, I found the reason, exception was handled as it should but there was not content. I wrongly assume that it's something with Classes, but simply the Slim code have bugs.
public function halt( $status, $message = '' ) {
$this->cleanBuffer();
$this->response->status($status);
$this->response->body($message);
$this->stop();
}
function overwrite data that you use in the handler like this
$app->notFound(function() {
$app->response()->body('Error');
});
won't work, function not found should look like this
public function notFound($callable = null) {
if (!is_null(($callable))) {
$this->router->notFound($this->auto_render_fun($callable));
} else {
$customNotFoundHandler = $this->router->notFound();
if (is_callable($customNotFoundHandler)) {
call_user_func($customNotFoundHandler);
} else {
call_user_func(array($this, 'defaultNotFound'));
}
$this->response->status(404);
$this->stop();
}
}

php typecast constructor

I would like to typecast PHP exceptions. Consider the following code:
class myException extends Exception {
function __construct( $mOrigin = "", $iCode = 0, Exception $oPrevious = null){
if(is_string($mOrigin)){
parent::__construct($mOrigin, $iCode, $oPrevious);
} elseif ($mOrigin instanceof Exception) {
parent::__construct($mOrigin->getMessage(),$mOrigin->getCode(),$mOrigin->getPrevious());
$this->file = $mOrigin->getFile();
$this->line = $mOrigin->getLine();
} else {
parent::__construct("\$mOrigin has wrong type", self::eFatal, $oPrevious);
}
}
The idea is to turn a standard Exception into a myException preserving the original stack trace. Since the variables holding the trace are private I cannot copy these values immediately and the CTOR produces a new one for myException.
The first idea was of course to use clone, but I can hardly re-assign $this, can I?
So what I'm trying to do is a C++ style typecast CTOR. Is there a sensible paradigm in PHP to do this?
Why not just set trace & previous the same way as file & line?
class myException extends Exception {
function __construct( $mOrigin = "", $iCode = 0, Exception $oPrevious = null){
if(is_string($mOrigin)){
parent::__construct($mOrigin, $iCode, $oPrevious);
} elseif ($mOrigin instanceof Exception) {
parent::__construct($mOrigin->getMessage(),$mOrigin->getCode(),$mOrigin->getPrevious());
$this->file = $mOrigin->getFile();
$this->line = $mOrigin->getLine();
$this->trace = $mOrigin->getTrace();
$this->previous = $mOrigin->getPrevious();
} else {
parent::__construct("\$mOrigin has wrong type", self::eFatal, $oPrevious);
}
}
EDIT:
See comments below regarding why I got away w/ this code earlier.
Why not turn your myException class into a decorator:
class myException extends Exception {
private $_oException;
function __construct( $mOrigin = "", $iCode = 0, Exception $oPrevious = null){
if(is_string($mOrigin)){
parent::__construct($mOrigin, $iCode, $oPrevious);
} elseif ($mOrigin instanceof Exception) {
$this->_oException = $mOrigin;
parent::__construct($mOrigin->getMessage(),$mOrigin->getCode(),$mOrigin->getPrevious());
$this->file = $mOrigin->getFile();
$this->line = $mOrigin->getLine();
} else {
parent::__construct("\$mOrigin has wrong type", self::eFatal, $oPrevious);
}
}
function getTrace()
{
return $this->_oException->getTrace();
}
function getPrevious()
{
return $this->_oException->getPrevious();
}
}
FUTHER INFO:
I've followed up on php-general and it turns out this is the intended behavior and it works the same in Java et al as well. You can override the member variable in child classes and have a separate store with the same name. This compiles just fine in java
public class PrivateAccess
{
private Boolean isAccessible = true;
public Boolean getAccessible()
{
return isAccessible;
}
}
class PrivateAccessChild extends PrivateAccess
{
private Boolean isAccessible = false;
public Boolean getAccessible()
{
return isAccessible;
}
public Boolean getParentAccessible()
{
return super.getAccessible();
}
public static void main(String[] args)
{
PrivateAccessChild pAccess = new PrivateAccessChild();
if(!pAccess.getAccessible())
System.out.println("we're hitting the child here...");
if(pAccess.getParentAccessible())
System.out.println("we're hitting the parent here...");
System.out.println("we're done here...");
}
}

Categories