I have the error
Call to undefined method Exception::message()
inside the object calling
Utils::message()
(I am catching the Exception and replacing by the message)
The file containing this object is included (require_once) by the top file together with another file defining Utils.
So my question is why the method of Utils is not recognized. All classes are included before the main code. Does the inclusion order matter? Any other hints?
EDIT. My Utils class:
class Utils {
private static $count;
private static $aggregateCount, $time, $last, $previous;
...
public static function message () {
echo "\n Count: ", self::$count++, " ";
$array = func_get_args();
foreach ($array as $entry) {
if (is_array($entry)) {
echo print_r($entry);
} else {
echo $entry;
}
}
}
...
}
And here is the function using the class:
public function updateManyWithId ($schema, array $bundle) {
foreach ($bundle as $hash) {
$id = $hash[ID_KEY];
$hash = $this->_adjustId($schema, $hash);
try {
$this->update($schema, $id, $hash);
} catch (Exception $e) {
Utils::message("Cannot update hash: ", $hash, $e->message());
}
}
}
$e->message() Is the problem. That refers to a class called Exception which is expected to have a method called message, but your Exception class does not have that method.
Look at the constructor of your Exception. You're passing in a message when you throw it, perhaps you're looking for $e->getMessage().
Read here: http://www.php.net/manual/en/exception.getmessage.php
To summarize, consider this:
class Exception {
//construct is used when you throw new Exception (instantiate the class)
public function __construct($message) {
$this->message = $message; //now the object has a "message" property
}
//this is a "getter" for this object's message
public function getMessage() {
return $this->message; //return the message
}
}
Note that the standard usage of getters/setters is to use getProperty and setProperty as the method name that returns/sets that property.
Related
I could use RefexionFunction outside of a class, but inside a class I get an exception.
Fatal error: Uncaught ReflectionException: Function Test::test_function() does not exist in test.php.
<?php
function parameters($functionName,$args){
$f = new ReflectionFunction($functionName);
....
}
class Test{
public function test_functionA($abc,$d,$e,$f) {
parameters(__METHOD__,func_get_args());
}
protected function test_functionB($abc,$d,$e,$f) {
parameters(__METHOD__,func_get_args());
}
private function test_functionC($abc,$d,$e,$f) {
parameters(__METHOD__,func_get_args());
}
}
$test = new Test();
$test->test_function('something',1,2,array(123,456));
?>
Appreciate your help.
Your error:
Fatal error: Uncaught ReflectionException: Function Test::test_function() does not exist in test.php.
Doesn't refer to the function name quite as you expect it to.
ReflectionClass docs says this:
The ReflectionClass class reports information about a class.
ref: https://secure.php.net/manual/en/class.reflectionclass.php
You want to use a combination of methods available in that class to get information about the passed method like this:
public function parameters($class, $fnc)
{
$f = new ReflectionClass($class);
if ($f->hasMethod($fnc)) {
return 'howdy folks';
} else {
return 'not so howdy folks';
}
}
You first pass the class before checking if the function exists. You can then use the built-in function hasMethod to check if the function exists. You then use the parameters function like this:
public function testFunction()
{
return $this->helper->parameters(__CLASS__, __FUNCTION__);
}
All together the code looks like this:
<?php
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
error_reporting(-1);
class paramsHelper
{
public function parameters($class, $fnc)
{
$f = new ReflectionClass($class);
$f->getMethod($fnc);
if ($f->hasMethod($fnc)) {
return 'howdy folks';
} else {
return 'not so howdy folks';
}
return $f;
}
}
class Test
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function testFunction()
{
return $this->helper->parameters(__CLASS__, __FUNCTION__);
}
}
$test = new Test(new paramsHelper());
echo '<pre>';
print_r($test->testFunction());
echo '</pre>';
One of your other problems is that __METHOD__ actually returns a string like this: Test::testFunction not testFunction - hence my use of __FUNCTION__ instead.
Edit:
To get the parameters of the passed method, change your parameters method to this:
class paramsHelper
{
public function getMethodParameters($class, $fnc)
{
$f = new ReflectionMethod($class, $fnc);
echo '<pre>';
print_r($f->getParameters());
echo '</pre>';
}
}
This uses ReflectionMethod in place of ReflectionClass - this is more inline with your intended use.
ref: https://secure.php.net/manual/en/class.reflectionmethod.php
use:
class paramsHelper
{
public function getMethodParameters($class, $fnc)
{
$f = new ReflectionMethod($class, $fnc);
echo '<pre>';
print_r($f->getParameters());
echo '</pre>';
}
}
class Test
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function testFunction($a = '', $b = 1, $c = 3)
{
return $this->helper->parameters(__CLASS__, __FUNCTION__);
}
}
$test = new Test(new paramsHelper());
echo '<pre>';
print_r($test->testFunction());
echo '</pre>';
I want to modify this code:
function send($message, $mode, $param1, $param2)
{
$msg = ">> " . $message;
if ($mode == "client") {
$client = $param1; // $param1 is a websocket source variable
// code
}
elseif ($mode == "clients") {
$clients = $param1; // now $param1 is an array of websocket sources
// code
}
elseif ($mode == "all") {
// code
}
}
send("Hello World!", "all", $whatever1, $whatever2);
(this function actually reads $mode to understand what it is going to do)
to the code below.
This code WILL NOT work. I would you like to tell me what changes i have to do for it to work
class send($message)
{
$msg = ">> " . $message;
public function client($client, $param2) { // $client is $param1 of previous code
// code using $msg
}
public function clients($clients, $param2) { // $clients is an array and the $param1 of previous code
// code using $msg
}
public function all($param2) {
// code using $msg
}
}
send("Hello World!")::all($whatever2);
I know the second code is very messed up. It doesn't work, but I want to do something like this. To categorize functions and parameters. I hope you got the idea. Maybe there is no such way and I have to use the $mode method in the first code?
You are trying to decompose the function and create a class from the parts.
There are some syntax errors in your code.
You are mixing up a lot of concepts here: functions, classes, static and dynamic calls. Please read the basic chapters in the PHP manual about OOP: http://php.net/manual/en/language.oop5.php
This part is wrong.
class send($message)
{
A class definition begins with the keyword class, followed by a class name:
class Send {
}
You can not use this directly inside a class, you could wrap it in the constructor or in a function.
$msg = ">> " . $message;
You can declare a constructor which accepts a parameter:
public function __construct($message) {
$this->message = $message;
}
class Send
{
private $message = '';
public function __construct($message) {
$this->message = $message;
}
public function toClient($client, $param) {
var_dump($this->message, $client, $param);
}
public function toClients($clients, $param) {
var_dump($this->message, $clients, $param);
}
public function toAll($param) {
var_dump($this->message, $param);
}
}
This class accept the message in the constructor and sets it as a property.
You might then reuse it across the functions ($this->message).
// Usage
$message = 'Hello World';
// instantiate the class
// and pass the message you want to send as param
// execute a specific class method
$send = new Send($message);
$send->toClient('john', 'more');
// with PHP 5.4 it's a one-liner
(new Send($message))->toClient('john', 'more');
I figured it out, many many thanks to Jens A. Koch
For all of you out there:
Prior to PHP 5.4:
class send {
public function __construct($msg) {
$this -> message = $msg;
}
public function clients(/* many params */) {
echo "some clients say: ".$this->message;
}
public function client(/* many params */) {
echo "one client says: ".$this->message;
}
public function all(/* many params */) {
echo "all clients say: ".$this->message;
}
// function b (build) returns the class
public function b($msg) {return new self($msg);}
}
// CALLING IT
send::b("test")->clients(); // >> some clients say: test
PHP 5.4+ (unfortunately I have 5.3 but it should work)
"public function b" is not needed:
class send {
public function __construct($msg) {
$this -> message = $msg;
}
public function clients(/* many params */) {
echo "some clients say: ".$this->message;
}
public function client(/* many params */) {
echo "one client says: ".$this->message;
}
public function all(/* many params */) {
echo "all clients say: ".$this->message;
}
}
// CALLING IT
(new send("test"))->clients();
Lets say I have:
class Zebra{
public static function action(){
print 'I was called from the '.get_class().' class'; // How do I get water here?
}
}
class Water{
public static function drink(){
Zebra::action();
}
}
Water::drink();
How do I get "water" from the zebra class?
(This is for php 5.3)
You can get the caller's info from debug_backtrace http://php.net/manual/en/function.debug-backtrace.php
One not so good solution is :
use __METHOD__ or __FUNCTION__ or __CLASS__ .
and pass it as parameter to function being called.
http://codepad.org/AVG0Taq7
<?php
class Zebra{
public static function action($source){
print 'I was called from the '.$source.' class'; // How do I get water here?
}
}
class Water{
public static function drink(){
Zebra::action(__CLASS__);
}
}
Water::drink();
?>
Full usable solution using exception, but not debug_backtrace, no need to modify any prototype :
function getRealCallClass($functionName)
{
try
{
throw new exception();
}
catch(exception $e)
{
$trace = $e->getTrace();
$bInfunction = false;
foreach($trace as $trace_piece)
{
if ($trace_piece['function'] == $functionName)
{
if (!$bInfunction)
$bInfunction = true;
}
elseif($bInfunction) //found !!!
{
return $trace_piece['class'];
}
}
}
}
class Zebra{
public static function action(){
print 'I was called from the '.getRealCallClass(__FUNCTION__).' class';
}
}
class Water{
public static function drink(){
Zebra::action();
}
}
Water::drink();
I'm trying to add methods dynamically from external files.
Right now I have __call method in my class so when i call the method I want, __call includes it for me; the problem is I want to call loaded function by using my class, and I don't want loaded function outside of the class;
Class myClass
{
function__call($name, $args)
{
require_once($name.".php");
}
}
echoA.php:
function echoA()
{
echo("A");
}
then i want to use it like:
$myClass = new myClass();
$myClass->echoA();
Any advice will be appreciated.
Is this what you need?
$methodOne = function ()
{
echo "I am doing one.".PHP_EOL;
};
$methodTwo = function ()
{
echo "I am doing two.".PHP_EOL;
};
class Composite
{
function addMethod($name, $method)
{
$this->{$name} = $method;
}
public function __call($name, $arguments)
{
return call_user_func($this->{$name}, $arguments);
}
}
$one = new Composite();
$one -> addMethod("method1", $methodOne);
$one -> method1();
$one -> addMethod("method2", $methodTwo);
$one -> method2();
You cannot dynamically add methods to a class at runtime, period.*
PHP simply isn't a very duck-punchable language.
* Without ugly hacks.
You can dynamically add attributes and methods providing it is done through the constructor in the same way you can pass a function as argument of another function.
class Example {
function __construct($f)
{
$this->action=$f;
}
}
function fun() {
echo "hello\n";
}
$ex1 = new class('fun');
You can not call directlry $ex1->action(), it must be assigned to a variable and then you can call this variable like a function.
if i read the manual right,
the __call get called insted of the function, if the function dosn't exist
so you probely need to call it after you created it
Class myClass
{
function __call($name, $args)
{
require_once($name.".php");
$this->$name($args);
}
}
You can create an attribute in your class : methods=[]
and use create_function for create lambda function.
Stock it in the methods attribute, at index of the name of method you want.
use :
function __call($method, $arguments)
{
if(method_exists($this, $method))
$this->$method($arguments);
else
$this->methods[$method]($arguments);
}
to find and call good method.
What you are referring to is called Overloading. Read all about it in the PHP Manual
/**
* #method Talk hello(string $name)
* #method Talk goodbye(string $name)
*/
class Talk {
private $methods = [];
public function __construct(array $methods) {
$this->methods = $methods;
}
public function __call(string $method, array $arguments): Talk {
if ($func = $this->methods[$method] ?? false) {
$func(...$arguments);
return $this;
}
throw new \RuntimeException(sprintf('Missing %s method.'));
}
}
$howdy = new Talk([
'hello' => function(string $name) {
echo sprintf('Hello %s!%s', $name, PHP_EOL);
},
'goodbye' => function(string $name) {
echo sprintf('Goodbye %s!%s', $name, PHP_EOL);
},
]);
$howdy
->hello('Jim')
->goodbye('Joe');
https://3v4l.org/iIhph
You can do both adding methods and properties dynamically.
Properties:
class XXX
{
public function __construct($array1)
{
foreach ($array1 as $item) {
$this->$item = "PropValue for property : " . $item;
}
}
}
$a1 = array("prop1", "prop2", "prop3", "prop4");
$class1 = new XXX($a1);
echo $class1->prop1 . PHP_EOL;
echo $class1->prop2 . PHP_EOL;
echo $class1->prop3 . PHP_EOL;
echo $class1->prop4 . PHP_EOL;
Methods:
//using anounymous function
$method1 = function () {
echo "this can be in an include file and read inline." . PHP_EOL;
};
class class1
{
//build the new method from the constructor, not required to do it here by it is simpler.
public function __construct($functionName, $body)
{
$this->{$functionName} = $body;
}
public function __call($functionName, $arguments)
{
return call_user_func($this->{$functionName}, $arguments);
}
}
//pass the new method name and the refernce to the anounymous function
$myObjectWithNewMethod = new class1("method1", $method1);
$myObjectWithNewMethod->method1();
I've worked up the following code example and a helper method which works with __call which may prove useful. https://github.com/permanenttourist/helpers/tree/master/PHP/php_append_methods
I'm looking for something like break for loops.
Here's some example code (using Symfony's lime) where stop() would not let the class continue and I_DONT_WANT_THIS_TO_RUN() would not be executed.
$browser->isStatusCode(200)
->isRequestParameter('module', 'home')
->isRequestParameter('action', 'index')
->click('Register')
->stop()
->I_DONT_WANT_THIS_TO_RUN();
$browser->thenThisRunsOkay();
Calling $this->__deconstruct(); from within stop() doesn't seem to do the trick. Is there a function I can call within stop() that would make that happen?
You could use PHP exceptions:
// This function would of course be declared in the class
function stop() {
throw new Exception('Stopped.');
}
try {
$browser->isStatusCode(200)
->isRequestParameter('module', 'home')
->isRequestParameter('action', 'index')
->click('Register')
->stop()
->I_DONT_WANT_THIS_TO_RUN();
} catch (Exception $e) {
// when stop() throws the exception, control will go on from here.
}
$browser->thenThisRunsOkay();
Just return another class which will return $this for every method called.
Example:
class NoMethods {
public function __call($name, $args)
{
echo __METHOD__ . " called $name with " . count($args) . " arguments.\n";
return $this;
}
}
class Browser {
public function runThis()
{
echo __METHOD__ . "\n";
return $this;
}
public function stop()
{
echo __METHOD__ . "\n";
return new NoMethods();
}
public function dontRunThis()
{
echo __METHOD__ . "\n";
return $this;
}
}
$browser = new Browser();
echo "with stop\n";
$browser->runThis()->stop()->dontRunThis()->dunno('hey');
echo "without stop\n";
$browser->runThis()->dontRunThis();
echo "the end\n";
Will result in:
with stop
Browser::runThis
Browser::stop
NoMethods::__call called dontRunThis with 0 arguments.
NoMethods::__call called dunno with 1 arguments.
without stop
Browser::runThis
Browser::dontRunThis
the end
OIS's answer is really good, though I could see that it might get confusing if the object suddenly changes to something else. That is, you'd expect that at the end of your chain, you'll end up with the same object. To avoid that problem, I'd add a private variable to tell the class whether or not to actually do anything. If the class has been stopped, then every class just returns $this straight away. This gives you the added benefit of being able to restart the execution.
class MyClass {
private $halt;
function __call($func, $args) {
if ($this->halt) {
return $this;
} else {
return $this->$func($args);
}
}
private function isRequestParameter() {
// ...
}
public function stop() {
$this->halt = true;
}
public function start() {
$this->halt = false;
}
}
This could be put into a parent class so you don't have to duplicate this code.