Undefined property of object when using define() - php

Why can I not do something like the following when I know that the property of myobject is going to be declared already:
define('title','boats');
myobject->title;
but this works:
myobject->boats
Is this even good practice?

You can't use
$myobject->title
as this is attempting to access the title property of your object. If this property does not exist, an error will be triggered.
You can use
$myobject->{title}
but I'd rather see you use a variable instead of a constant, eg
$title = 'boats';
echo $myobject->$title;
Ideally, you will know which property you want to access and use its name appropriately
$myobject->boats

Why can I not do something like the following when I know that the property of myobject is going to be declared already:
Probably because PHP expects you to call it with a method name. There are options, though:
<?php
define( 'METHOD', 'bar' );
class Foo {
public function bar( ) {
echo "Foo->bar( ) called.\n";
}
}
$foo = new Foo;
call_user_func( array( $foo, METHOD ) );
// or
$method = METHOD;
$foo->$method( );
EDIT: Ah, I seem to have misunderstood. My version is for calling methods of which the name is defined in a constant, whereas you were looking for a way of calling properties. Well, I'll leave it in here for future reference anyway.

Related

About arbitrary class properties in PHP

I have just learned that you can set any property of a class (as long as it's not explicitly declared as a private one). For example:
class Test {}
$test = new Test;
$test->randomthing = 'Hello world';
var_export($test);
Works just fine to set the property randomthing of the $test object.
I am trying to find out what this behaviour is called, and if it's possible to eliminate/disable it (to minimise errors in code), because as it currently stands, i can be typoing my desired properties and not notice in time.
Ideally, i would want to have:
class User {
protected $name;
}
And then i want to be able to set a $user->name = '', but not $user->randomthing = ''. This second call would ideally throw an exception or something.
I have tried to guess this convention by looking up "dynamic properties", but that seems to refer to stuff like the double-dollar syntax to reference a property by a value in a variable.
So, what do you call this behaviour? And where can i read up more about controlling it (or disabling it)?
It doesn't have a special name or anything, it's just assigning a property to an object. This is standard and default PHP - to my knowledge, this isn't something you can disable/turn off.
All we're doing $object->newProp = 'foo' is appending a new key with a value to the object, that key + value pair isn't globally available once it's been added. It's only for that object. It's the same principle for an array:
$array = [1, 2, 3];
$array[] = 4;
We're doing the same with your object:
$object = new Class();
$object->foo = 4;
I would create a "magic" __set() function that doesn't set a parameter, but instead returns an error/warning.
public function __set ( string $name , mixed $value ) {
if( in_array( $name, [ 'various', 'valid', 'properties' ] ) ) {
$this->$name = $value;
} else {
trigger_error( "Setting unknown property ".__CLASS__."->$name is not permitted." );
}
}
https://www.php.net/manual/en/language.oop5.overloading.php#object.set

Is is possible to store a reference to an object method?

Assume this class code:
class Foo {
function method() {
echo 'works';
}
}
Is there any way to store a reference to the method method of a Foo instance?
I'm just experimenting and fiddling around, my goal is checking whether PHP allows to call $FooInstance->method() without writing $FooInstance-> every time. I know I could write a function wrapper for this, but I'm more interested in getting a reference to the instance method.
For example, this pseudo-code would theoretically store $foo->method in the $method variable:
$foo = new Foo();
$method = $foo->method; //Undefined property: Foo::$method
$method();
Apparently, as method is a method and I'm not calling it with () the interpreter thinks I'm looking for a property thus this doesn't work.
I've read through Returning References but the examples only show how to return references to variables, not methods.
Therefore, I've adapted my code to store an anonymous function in a variable and return it:
class Foo {
function &method() {
$fn = function() {
echo 'works';
};
return $fn;
}
}
$foo = new Foo();
$method = &$foo->method();
$method();
This works, but is rather ugly. Also, there's no neat way to call it a single time, as this seems to require storing the returned function in a variable prior to calling it: $foo->method()(); and ($foo->method())(); are syntax errors.
Also, I've tried returning the anonymous function directly without storing it in a variable, but then I get the following notice:
Notice: Only variable references should be returned by reference
Does this mean that returning/storing a reference to a class instance method is impossible/discouraged or am I overlooking something?
Update: I don't mind adding a getter if necessary, the goal is just getting a reference to the method. I've even tried:
class Foo {
var $fn = function() {
echo 'works';
};
function &method() {
return $this->fn;
}
}
But from the unexpected 'function' (T_FUNCTION) error I'd believe that PHP wisely doesn't allow properties to store functions.
I'm starting to believe that my goal isn't easily achievable without the use of ugly hacks as eval().
It is. You have to use an array, with two values: the class instance (or string of the class name if you are calling a static method) and the method name as a string. This is documented on the Callbacks Man page:
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
Demo (Codepad):
<?php
class Something {
public function abc() {
echo 'called';
}
}
$some = new Something;
$meth = array($some, 'abc');
$meth(); // 'called'
Note this is also works with the built-ins that require callbacks (Codepad):
class Filter {
public function doFilter($value) {
return $value !== 3;
}
}
$filter = new Filter;
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array($filter, 'doFilter'))); // 'array(1,2,4,5)'
And for static methods -- note the 'Filter' instead of an instance of a class as the first element in the array (Codepad):
class Filter {
public static function doFilter($value) {
return $value !== 3;
}
}
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array('Filter', 'doFilter'))); // 'array(1,2,4,5)'
// -------- or -----------
var_dump(array_filter($test, 'Filter::doFilter')); // As of PHP 5.2.3
Yes, you can. PHP has a "callable" pseudo-type, which is, in fact, either just a string or an array. Several functions (usort comes to mind) accept a parameter of the "callback" type: in fact, they just want a function name, or an object-method pair.
That's right, strings are callable:
$fn = "strlen";
$fn("string"); // returns 6
As mentioned, it's possible to use an array as a callback, too. In that case, the first element has to be an object, and the second argument must be a method name:
$obj = new Foo();
$fn = array($obj, "method");
$fn(); // calls $obj->method()
Previously, you had to use call_user_func to call them, but syntax sugar in recent versions make it possible to perform the call straight on variables.
You can read more on the "callable" documentation page.
No, as far as I know it's not possible to store a reference to a method in PHP. Storing object / class name and a method name in an array works, but it's just an array without any special meaning. You can play with the array as you please, for example:
$ref = [new My_Class(), "x"];
// all is fine here ...
$ref();
// but this also valid, now the 'reference' points to My_Other_Class::x()
// do you expect real reference to behave like this?
$ref[0] = new My_Other_Class();
$ref();
// this is also valid syntax, but it throws fatal error
$ref[0] = 1;
$ref();
// let's assume My_Class::y() is a protected method, this won't work outside My_Class
$ref = [new My_Class(), 'y'];
$ref();
this is prone to error as you loose syntax checking due to storing the method name as string.
you can't pass reliably a reference to a private or a protected method this way (unless you call the reference from a context that already has proper access to the method).
Personally I prefer to use lambdas:
$ref = function() use($my_object) { $my_object->x(); }
If you do this from inside $my_object it gets less clunky thanks to access to $this:
$ref = function() { $this->x(); }
this works with protected / private methods
syntax checking works in IDE (less bugs)
unfortunately it's less concise

Access class constant and static method from string

I have a string containing the class name and I wish to get a constant and call a (static) method from that class.
<?php
$myclass = 'b'; // My class I wish to use
$x = new x($myclass); // Create an instance of x
$response = $x->runMethod(); // Call "runMethod" which calls my desired method
// This is my class I use to access the other classes
class x {
private $myclass = NULL;
public function __construct ( $myclass ) {
if(is_string($myclass)) {
// Assuming the input has a valid class name
$this->myclass = $myclass;
}
}
public function runMethod() {
// Get the selected constant here
print $this->myclass::CONSTANT;
// Call the selected method here
return $this->myclass::method('input string');
}
}
// These are my class(es) I want to access
abstract class a {
const CONSTANT = 'this is my constant';
public static function method ( $str ) {
return $str;
}
}
class b extends a {
const CONSTANT = 'this is my new constant';
public static function method ( $str ) {
return 'this is my method, and this is my string: '. $str;
}
}
?>
As I expected (more or less), using $variable::CONSTANT or $variable::method(); doesn't work.
Before asking what I have tried; I've tried so many things I basically forgot.
What's the best approach to do this? Thanks in advance.
To access the constant, use constant():
constant( $this->myClass.'::CONSTANT' );
Be advised: If you are working with namespaces, you need to specifically add your namespace to the string even if you call constant() from the same namespace!
For the call, you'll have to use call_user_func():
call_user_func( array( $this->myclass, 'method' ) );
However: this is all not very efficient, so you might want to take another look at your object hierarchy design. There might be a better way to achieve the desired result, using inheritance etc.
in php 7 you can use this code
echo 'my class name'::$b;
or
#Uncomment this lines if you're the input($className and $constName) is safe.
$reg = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/';
if(preg_match($reg,$className) !== 1 || preg_match($reg,$constName) !== 1)
throw new \Exception('Oh, is it an attack?');
$value = eval("return $className::$constName;");
You can achieve it by setting a temporary variable. Not the most elegant way but it works.
public function runMethod() {
// Temporary variable
$myclass = $this->myclass;
// Get the selected constant here
print $myclass::CONSTANT;
// Call the selected method here
return $myclass::method('input string');
}
I guess it's to do with the ambiguity of the ::, at least that what the error message is hinting at (PHP Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM)
Use call_user_func to call static method:
call_user_func(array($className, $methodName), $parameter);
Classes defined as abstract may not be instantiated, and any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method's signature - they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility. For example, if the abstract method is defined as protected, the function implementation must be defined as either protected or public, but not private. Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. This also applies to constructors as of PHP 5.4. Before 5.4 constructor signatures could differ.
Refer to http://php.net/manual/en/language.oop5.abstract.php
This might just be tangential to the subject but, while searching for my own issue I found that the accepted answer pointed me in the right direction, so I wanted to share my problem & solution in case someone else might be stuck in a similar fashion.
I was using the PDO class and was building some error options from an ini config file. I needed them in an associative array in the form: PDO::OPTION_KEY => PDO::OPTION_VALUE, but it was of course failing because I was trying to build the array with just PDO::$key => PDO::$value.
The solution (inspired from the accepted answer):
$config['options'] += [constant('PDO::'.$key) => constant('PDO::'.$option)];
where everything works if you concatenate the class name and the Scope Resolution Operator as a string with the variable and get the constant value of the resulting string through the constant function (more here).
Thank you and I hope this helps someone else!

Can a PHP object respond to an undefined method?

Rails relies on some of the neat aspects of Ruby. One of those is the ability to respond to an undefined method.
Consider a relationship between Dog and Owner. Owner has_many :dogs and Dog belongs_to :owner.
If you go into script/console, get a dog object with fido = Dog.find(1), and look at that object, you won't see a method or attribute called Owner.
What you will see is an owner_id. And if you ask for fido.owner, the object will do something like this (at least, this is how it appears to me):
I'm being asked for my .owner attribute. I don't have one of those!
Before I throw a NoMethodError, do I have a rule about how to deal with this?
Yes, I do: I should check and see if I have an owner_id.
I do! OK, then I'll do a join and return that owner object.
PHP's documentation is - ahem - a bit lacking sometimes, so I wonder if anyone here knows the answer to this:
Can I define similar behavior for objects in PHP?
If not, do you know of a workaround for flexible model joins like these?
You can implement the __call() method in PHP which is a catch all for calling an otherwise inaccessible method.
class MyObj {
public function __call($name, $args) {
$list = $args ? '"' . implode('", "', $args) . '"' : '';
echo "Call $name($list)\n";
}
}
$m = new MyObj;
$m->method1(1, 2, 3);
$m->method2();
Some languages (eg Javascript) also have what are called first-class functions. This essentially allows you to add or remove methods from objects (or classes) on the fly. PHP syntax (as of 5.3) sort of supports this but it isn't really usable.
$obj = new stdClass;
$obj->foo = function() {
echo "hello\n";
};
print_r($obj);
Output:
stdClass Object
(
[foo] => Closure Object
(
)
)
But try:
$obj->foo();
and you get:
Fatal error: Call to undefined method stdClass::foo() in C:\xampp\htdocs\test.php on line 8
However:
$f = $obj->foo;
$f();
correctly outputs hello.
Overloading to the rescue!
After some further research, it appears that what I really wanted was PHP's overloading methods. The code at the link Gordon gave, and especially the downloadable example they offer, was very illuminating.
(Despite my question title, what I was really after was to have an object respond to undefined attributes.)
So, __get() and __set() let you specify methods to use for getting and setting object attributes, and within those methods, you can tell the object what to do if no such attribute exists. Let's just look at __get() for now.
Going back to my Dog example, you could use it like this:
class Dog{
// Private attributes, not accessible except through the __get method
private $bark_volume = 'loud';
private $owner_id = '5';
public function __get($name){
// If there's a property by that name, return it
if (isset($this->$name)){
return $this->$name;
}
// If not, let's see if there's an id with a related name;
// if you ask for $this->owner, we'll check for $this->owner_id
$join_id = $name . "_id";
if(isset($this->$join_id)){
// This is pretty useless, but the id could be used
// to do a join query instead
return $this->$join_id;
}
}
}
$Fido = new Dog;
echo $Fido->bark_volume; //outputs 'loud'
echo '<br/>';
echo $Fido->owner; //outputs '5'

How to call classes from a variable variable?

I'm not quite sure how to correctly put this question. I want to dynamically call functions that are contained in classes (I think this means they are called 'methods').
Here is an example of my code which I hope helps explain what I am trying to achieve.
In this instance $result returns all the different modules that are loaded. This then checks if the module's PHP file has been included with it's class, then if that class exists - trys to call the class directly.
foreach ($results as $result) {
$moduleclass_name = 'TestClassName_' . $result->module_name . '::FunctionToCall';
if (method_exists($moduleclass_name, 'FunctionToCall'))
$VariableToRetrieve = $modulefunction_name($Parameter1, $Parameter2);
}
This returns an error
"Call to undefined function
TestClassName_modulename::FunctionToCall()"
although the 'TestClassName' has been declared correctly.
Can someone tell me what I'm doing wrong?
What you want is probably call_user_func_array().
The code would look similar to this:
call_user_func_array(array($classNameOrInstance, $functionName), array($arg1, $arg2, $arg3));
EDIT Also, in your example you seem to have included the function name in the class parameter for method_exists, too...
You may use call_user_func() as such to achieve what you are trying to do. Also, it is better to use is_callable() instead of method_exists() to validate if the method is callable (method may exist but its visibility may prevent it from being callable.
foreach ($results as $result) {
$module_callback = array('TestClassName_' . $result->module_name,'FunctionToCall');
if (is_callable($module_callback))
$VariableToRetrieve = call_user_func($module_callback, $Parameter1, $Parameter2);
}
I think it doesn't work because your syntax may not support "static method calls".
I suggest you give a try to Franz's method call_user_func().
I did a similar thing on a former project.
It was designed to call a class which implemented an interface, so method names where known.
I don't think it's difficult to modify this code in order to make it match with yours.
class CDispatcher {
public static function GetDispatcher( $module = 'core' ) {
$class_name = $module . 'Dispatcher';
try {
// looks for the file associated with the class
// if the file is not found an exception is raised
search_class( $class_name );
} catch ( exception $e ){
throw new UnkwownModuleException($module);
}
return new $class_name();
}
}
// Then, you call this class :
$new_instance = CDispatcher::GetDispatcher( $my_module );

Categories