I'm new to PHP but have worked with JavaScript quite a bit. I was trying to do the following:
class MyClass {
private $someAnonymousFunction = function($any){
return $any;
};
$data = [
['some String', $someAnonymousFunction]
];
}
But it comes up with an error when I create someAnonymousFunction saying it doesn't like that I put function (unexpected 'function' (T_FUNCTION)) in there like that. I've tried different scenarios beyond the above example but it seems the same errors happen.
Update Why I want to do this. I want to do this because I want to abstract away much of the boiler plate in creating classes. If I'm going to be writing code over and over, I would like to make it at easy and straight forward as possible. Below is my code in full (note that this was just for class and I'm taking it far beyond what it needs to be):
abstract class Protection {
const Guard = 0;
const Open = 1;
}
abstract class __ {
public function identity($any) {
return $any;
}
}
// This is the core logic behind the class
trait Property {
public function get($name) {
if ($this->data[$name][1] == Protection::Open) {
return $this->data[$name][0];
}
else {
//throw error
}
}
public function set($name, $set) {
if ($this->data[$name][1] == Protection::Open) {
//Guard function can throw error or filter input
$func = $this->data[$name][2];
$this->data[$name][0] = $func($set);
return $this;
}
else {
// throw error
}
}
}
class Monster {
use Property;
//Just trying to get this to work. Throws error.
private $identity = function($any) {
return $any;
};
private $data = [
'hairColor' => [
'brown',
Protection::Open,
ucwords
],
'killType' => [
'sword',
Protection::Open,
__::identity //Doesn't work either thinks it's a constant.
]
];
}
$generic = new Monster();
echo '<br> Hair color: ' . $generic->get('hairColor') . ', Kill type: '
. $generic->get('killType') . '<br>';
$generic->set('hairColor', 'blue')->set('killType', 'silver bullet');
Update 2: Here's the "final" code:
<?php
class Utils {
static function identity($any) {
return $any;
}
}
// Property abstracts much of the boiler plate away
// from making classes with getters and setters.
// It is chainable by default.
// It takes a variable named `data` which holds an
// associative array, with the keys being the names
// of the properties/methods. The key holds a value
// which is an indexed array where:
// Index 0 == Value of property.
// [Index 1] == Optional string name of guard function.
// [Index 2] == Optional string name of method called
// with `get` method.
// It has two public methods:
// `get` returns value of property or calls a method.
// `set` gives a new value to a property.
trait Property {
// Create a new property with attributes in an array.
private function createNewProperty($name, $set) {
$this->data[$name] = (is_array($set)) ? $set : [$set];
}
// Return property value
public function get($name) {
// If the property doesn't exist throw an error.
if (!isset($this->data[$name])) {
throw new Exception('No such property or method '.$name);
}
// copy by reference value into differently
// named variable to make code more concise.
$prop =& $this->data[$name];
// determine if property is a method.
if (isset($prop[2]) && $prop[2]) {
// call method with property value
return call_user_func($prop[2], $prop[0]);
}
else {
//return plain property value
return $prop[0];
}
}
// Set property value
public function set($name, $set) {
// If property isn't set then create one
if (!isset($this->data[$name])) {
createNewProperty($name, $set);
}
// copy by reference value into differently
// named variable to make code more concise.
$prop =& $this->data[$name];
// determine if guards exist when setting property
if (isset($prop[1]) && $prop[1]) {
$prop[0] = call_user_func($prop[1], $set);
return $this; // make chainable
}
else {
// set plain property
$prop[0] = $set;
return $this; // make chainable
}
}
}
class Monster {
use Property;
private $data = [
'hairColor' => ['brown', ['Utils', 'identity']],
'killType' => ['sword', 'ucwords'],
'simple' => ['simple value', null, 'ucwords'],
];
}
$generic = new Monster();
echo '<br> Hair color: ' . $generic->get('hairColor') . ', Kill type: '
. $generic->get('killType') . '<br>';
$generic->set('hairColor', 'blue')->set('killType', 'silver bullet');
echo '<br> Hair color: ' . $generic->get('hairColor') . ', Kill type: '
. $generic->get('killType') . '<br>';
echo '<br>Simple: '.$generic->get('simple');
$generic->set('simple', 'simple value changed!');
echo '<br>Simple: '.$generic->get('simple');
?>
You cannot assign arbitrary expressions in a property declaration like that, only constant values, or with recent changes to the parser, certain constant-valued expressions.
However, declaring properties with an initial value is actually just shorthand for declaring the property and then initialising it in the constructor, since the initial assignment occurs whenever a new instance is created.
So the two classes below will behave identically:
class Foo1 {
private $bar = 42;
}
class Foo2 {
private $bar;
public function __construct() {
$this->bar = 42;
}
}
Since there is no restriction on the assignments you can make in the constructor, this allows you to rewrite your code as this (you were missing the access specifier on $data, so I've assumed private):
class MyClass {
private $someAnonymousFunction;
private $data;
public function __construct() {
$this->someAnonymousFunction = function($any){
return $any;
};
$this->data = [
['some String', $this->someAnonymousFunction]
];
}
}
You can only initialize a field with a compile-time constant value. I am guessing that an anonymous function doesn't count as a constant value.
Related
I wonder whether this is a bug or normal. Let’s say I have a class with some magical functions:
class Foo {
public function __toString() {
return '`__toString` called.';
}
public function __get($key) {
return '`__get(' . $key . ')` called.';
}
public function __invoke($x = "") {
return '`__invoke(' . $x . ')` called.';
}
}
And then create an instance in an object property like this:
$object = (object) [
'foo' => 'bar',
'baz' => new Foo
];
Then test it:
echo $object->baz;
echo $object->baz->qux;
echo $object->baz('%'); // :(
It is broken in the last echo: Call to undefined method stdClass::baz()
Currently, the only solution I can do is to store the __invoke part in a temporary variable and then call that variable as a function like this:
$x = $object->baz;
echo $x('%'); // :)
It works fine when I instantiate the class in an array property:
$array = [
'baz' => new Foo
];
echo $array['baz'];
echo $array['baz']->qux;
echo $array['baz']('%'); // :)
By the way, I need this ability on my object for something related to API:
$foo = (object) ['bar' => new MyClass];
echo $foo->bar; → should trigger __toString
echo $foo->bar->baz; → should trigger __get
echo $foo->bar(); → should trigger __invoke
echo $foo->bar->baz(); → should trigger __call
All of them should return a string.
Can this be done in PHP completely? Thanks.
No can do.
The line in question is simply ambigous, and the error message shows you how ... It is more logical to try to access the baz() method of your $object object.
That's just the context given by the parser when it sees $object->baz()
As already mentioned in the comments, you can remove that ambiguity, help the parser by telling it that $object->baz is itself an expression that needs to be executed first:
($object->baz)('arg');
PHP is also itself a program, and has to know how to execute something before executing it. If it could blindly try every possible "magic" method on every object in a $foo->bar->baz->qux chain, then it wouldn't be able to tell you what the error is when it is encountered - it would just silently crash.
I have solved my problem by detecting the existence of an __invoke method inside the __call method of a class.
class MyStdClass extends stdClass {
protected $data = [];
public function __construct(array $array) {
$this->data = $array;
}
public function __get($key) {
return isset($this->data[$key]) ? $this->data[$key] : null;
}
public function __call($key, $args = []) {
if (isset($this->data[$key])) {
$test = $this->data[$key];
// not an object = not an instance, skip!
if (!is_object($test)) {
return $this->__get($key);
}
if (!empty($args) && get_class($test) && method_exists($test, '__invoke')) {
// or `return $test(...$args)`
return call_user_func([$test, '__invoke'], ...$args);
}
}
return $this->__get($key);
}
public function __set($key, $value = null) {
$this->data[$key] = $value;
}
public function __toString() {
return json_encode($this->data);
}
public function __isset($key) {}
public function __unset($key) {}
}
So, instead of converting the array into object with (object), here I use:
$object = new MyStdClass([
'foo' => 'bar',
'baz' => new Foo
]);
I have the following class:
/**
* #property int $barMagic
*/
class Foo
{
public $barNormal;
private $attributes = [];
public function __get($name) {
return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
}
public function __set($name, $value)
{
$this->attributes[$name] = $value;
}
}
As you can see, the $barMagic public property is not defined explicitly, it's accessed via the magic methods.
When setting and then modifying an array element in the normal attribute, it works fine:
$foo = new Foo();
$foo->barNormal = ['baz' => 1];
echo $foo->barNormal['baz'];
$foo->barNormal['baz'] = 2;
echo ',' . $foo->barNormal['baz'];
It outputs "1,2", just as intended.
But when using the magic property, it does not:
$foo = new Foo();
$foo->barMagic = ['baz' => 1];
echo $foo->barMagic['baz'];
$foo->barMagic['baz'] = 2;
echo ',' . $foo->barMagic['baz'];
It outputs "1,1"!
Is there a way in PHP to access array elements in magic properties the same way as normal ones?
The ArrayAccess interface seems to deal with array access one level higher than I need it.
The real answer is tricky and involves some bug/inconsistency in the PHP engine. As commentors suggested, I added the "&" (return by reference) character before __get(). So new code:
public function &__get($name) {
return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
}
but this gives
Notice: Only variable references should be returned by reference in ....
I had to change it to
public function &__get($name) {
if (isset($this->attributes[$name])) {
return $this->attributes[$name];
} else {
return null;
}
}
and now it works. Note that the two snippets should be completely equivalent, but they are not. Thank you all for the contribution, you took me halfway there.
Is there a way to create a class's constants dynamically? I know this sounds a bit odd but let me explain what I'm trying to do:
I have a Enum class who's attributes are defined by static const definitions
This class extends the PHP SplEnum class
Rather than type in each of these definitions in code I'd like to have a static initialiser go out to the database and pull the enumerated values
Maybe somethings like this:
class myEnum extends SplEnum {
public static function init () {
$myNameValuePair = DB_Functions::get_enum_list();
foreach ( $myNameValuePair as $name => $value) {
$const = array ( self , $name );
$const = $value;
}
}
}
I recognise that this won't actually work as it doesn't set CONST's but rather static variables. Maybe my whole idea is hair brained and there's a better technique to this. Anyway, any method to achieve the end goal is greatly appreciated.
UPDATE
I think it might be helpful to be a little more clear on my goals because I think it's entirely possibly that my use of Constants is not a good one. Basically I want to achieve is typical of the Enumerated list's requirements:
Constrain function signatures. I want to be able to ask for a "set" of values as an input to a function. For instance:
public function do_something ( ENUM_Types $type ) {}
Simple and Compact. Allow for a simple and compact syntax when used in code. For instance with the use of constants I might write a conditional statement something like:
if ( $my_var === ENUM_Types::TypeA ) {}
Dynamic enumeration. I'd like this enumeration to be managed through the frontend and stored in the database (I'm using wordpress admin screens for this in case anyone cares). At run time this "list" should be pulled out of the DB and made available to the code as an enumeration (or similar structure that achieves the goals above).
Wrap your "enum" values in a singleton and implement the (non-static) magic __get method:
<?php
class DynamicEnums {
private static $singleton;
private $enum_values;
public static function singleton() {
if (!self::$singleton) {
self::$singleton = new DynamicEnums();
}
return self::$singleton;
}
function __construct() {
$this->enum_values = array( //fetch from somewhere
'one' => 'two',
'buckle' => 'my shoe!',
);
}
function __get($name) {
return $this->enum_values[$name]; //or throw Exception?
}
public static function values() {
return self::singleton()->enum_values; //warning... mutable!
}
}
For bonus points, create a (non-OO) function that returns the singleton:
function DynamicEnums() {
return DynamicEnums::singleton();
}
Consumers of "DynamicEnums" would look like:
echo DynamicEnums::singleton()->one;
echo DynamicEnums()->one; //can you feel the magic?
print_r(DynamicEnums::values());
[edit] More enum-like.
Q: Is there a way to create a class's constants dynamically?
The answer is 'Yes', but don't do that :)
class EnumFactory {
public static function create($class, array $constants) {
$declaration = '';
foreach($constants as $name => $value) {
$declaration .= 'const ' . $name . ' = ' . $value . ';';
}
eval("class $class { $declaration }");
}
}
EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));
echo darkSide::FOO . ' ' . darkSide::BAR;
Next question...
Q: Constrain function signatures. I want to be able to ask for a "set" of values as an input to a function. For instance: public function do_something ( ENUM_Types $type ) {}
According to the manual, in that case $type is must be an instance of the ENUM_Types class. But for constant it is impossible (they can't contain objects).
But wait... We can use such trick:
class Enum {
protected static $_constantToClassMap = array();
protected static function who() { return __CLASS__; }
public static function registerConstants($constants) {
$class = static::who();
foreach ($constants as $name => $value) {
self::$_constantToClassMap[$class . '_' . $name] = new $class();
}
}
public static function __callStatic($name, $arguments) {
return self::$_constantToClassMap[static::who() . '_' . $name];
}
}
class EnumFactory {
public static function create($class, $constants) {
$declaration = '';
foreach($constants as $name => $value) {
$declaration .= 'const ' . $name . ' = ' . $value . ';';
}
eval("class $class extends Enum { $declaration protected static function who() { return __CLASS__; } }");
$class::registerConstants($constants);
}
}
EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));
EnumFactory::create('aaa', array('FOO' => 1, 'BAR' => 2));
echo (aaa::BAR() instanceof aaa) ? 'Yes' : 'No'; // Yes
echo (aaa::BAR() instanceof darkSide) ? 'Yes' : 'No'; // No
And after that we can use a "type hinting":
function doSomething(darkSide $var) {
echo 'Bu!';
}
doSomething(darkSide::BAR());
doSomething(aaa::BAR());
Q: Simple and Compact. Allow for a simple and compact syntax when used in code. For instance with the use of constants I might write a conditional statement something like: if ( $my_var === ENUM_Types::TypeA ) {}
You can use values of your pseudo-constants in such form:
if (darkSide::FOO === 1) {}
Q: Dynamic enumeration. I'd like this enumeration to be managed through the frontend and stored in the database (I'm using wordpress admin screens for this in case anyone cares). At run time this "list" should be pulled out of the DB and made available to the code as an enumeration (or similar structure that achieves the goals above).
You can init your enumeration by passing array to the EnumFactory::create($class, $constants):
EnumFactory::create('darkSide', array('FOO' => 1, 'BAR' => 2));
You could do something like Const = $$constant. Then you could set $contant = whatever. OR you could use a protected property since you want it to be dynamic and Constants are not. Example:
class Foo {
protected $test = '';
function set($bar){
$this->test = $bar;
}
function get($bar){
return $this->test;
}
}
$foobar = new Foo();
$foobar->set('test');
echo $foobar->get('test');
I do not recommend it, but eval() ... please don't.
I've modified autoloaders to automatically define Exception types that are missing or misspelled. Reason: You can catch an uncaught exception, but you cannot recover from the PHP_FATAL when instantiating a typo in your exception class.
So I have a couple of arrays
$array_1 = Array('one','two','three');
$array_2 = Array('red','blue','green');
Is there a dynamic way to create the Setters and Getters for an array with single value entries?
So the class would be something like:
class xFromArray() {
}
So the above if I passed $array_1 it would generate something like this:
private $one;
setOne($x) {
$one = $x;
}
getOne() {
return $one;
}
if I passed $array_2 it would generate something like this:
private $red;
setRed($x) {
$red = $x;
}
getRed() {
return $red;
}
So I would call it somehow like this? (My best guess but doesn't seem that this would work)
$xFromArray = new xFromArray;
foreach($array_1 as $key=>$data) {
$xFromArray->create_function(set.ucfirst($data)($data));
echo $xFromArray->create_function(get.ucfirst($data));
}
You can use __call() to invoke dynamic methods. So:
class Wrapper {
private $properties;
public function __construct(array $names) {
$this->properties = array_combine(array_keys($names),
array_fill(0, count($names), null));
}
public function __call($name, $args) {
if (preg_match('!(get|set)(\w+)!', $name, $match)) {
$prop = lcfirst($match[2]);
if ($match[1] == 'get') {
if (count($args) != 0) {
throw new Exception("Method '$name' expected 0 arguments, got " . count($args));
}
return $properties[$prop];
} else {
if (count($args) != 1) {
throw new Exception("Method '$name' expected 1 argument, got " . count($args));
}
$properties[$prop] = $args[0];
}
} else {
throw new Exception("Unknown method $name");
}
}
}
Personally I wouldn't go the route of using getters and setters in PHP. Use the special methods __get() and __set() instead and treat these dynamic properties as object properties rather than adding a (most likely unnecessary) method wrapper.
Edit: to clarify, __call() is invoked when you call an method in an object that either doesn't exist or is inaccessible. So:
$wrapper = new Wrapper($array_1);
$wrapper->setOne("foo");
echo $wrapper->getOne(); // foo
$wrapper->getAbc(); // exception, property doesn't exist
__call() is used here to decipher the method name. If it fits the pattern of get or set followed by a property name (from the initial array) then it works as expected, otherwise it throws an exception. You can of course change this behaviour any way you wish.
See Overloading from the PHP manual for a more detailed explanation of these "magic" methods.
You can use __call() (or __set() && __get()), but they have some overhead.
Assuming you have a constant defined in a class:
class Foo {
const ERR_SOME_CONST = 6001;
function bar() {
$x = 6001;
// need to get 'ERR_SOME_CONST'
}
}
Is it possible with PHP?
You can get them with the reflection API
I'm assuming you would like to get the name of the constant based on the value of your variable (value of variable == value of constant). Get all the constants defined in the class, loop over them and compare the values of those constants with the value of your variable.
Note that with this approach you might get some other constant that the one you want, if there are two constants with the same value.
example:
class Foo {
const ERR_SOME_CONST = 6001;
const ERR_SOME_OTHER_CONST = 5001;
function bar() {
$x = 6001;
$fooClass = new ReflectionClass ( 'Foo' );
$constants = $fooClass->getConstants();
$constName = null;
foreach ( $constants as $name => $value )
{
if ( $value == $x )
{
$constName = $name;
break;
}
}
echo $constName;
}
}
ps: do you mind telling why you need this, as it seems very unusual ...
Here's what I did to achieve it. Inspired by Jan Hancic.
class ErrorCode
{
const COMMENT_NEWCOMMENT_DISABLED = -4;
const COMMENT_TIMEBETWEENPOST_ERROR = -3;
/**
* Get error message of a value. It's actually the constant's name
* #param integer $value
*
* #return string
*/
public static function getErrorMessage($value)
{
$class = new ReflectionClass(__CLASS__);
$constants = array_flip($class->getConstants());
return $constants[$value];
}
}
With Reflection:
$class = new ReflectionClass("Foo");
$constants = $class->getConstants();
$constants is an array which holds all the names and values of the constants defined in class Foo.
All the other answers cover the essential points. But, if crazy one liners is your thing, then:
function getConstantName($class, $value)
{
return array_flip((new \ReflectionClass($class))->getConstants())[$value];
}
If you need to handle the case where the value might not actually be one of the constants, then you can give up an extra line:
function getConstantName($class, $value)
{
$map = array_flip((new \ReflectionClass($class))->getConstants());
return (array_key_exists($value, $map) ? $map[$value] : null);
}
I know this is an old question and all, but I still feel that I have some useful input. I implemented this using an abstract class that all my enums extend. The abstract class contains a generic toString() method;
abstract class BaseEnum{
private final function __construct(){ }
public static function toString($val){
$tmp = new ReflectionClass(get_called_class());
$a = $tmp->getConstants();
$b = array_flip($a);
return ucfirst(strtolower($b[$val]));
}
}
//actual enum
final class UserType extends BaseEnum {
const ADMIN = 10;
const USER = 5;
const VIEWER = 0;
}
This way you can get a human readable string to use in output, on every enum that extends the base enum. Furthermore, your implementation of the enum, being final, cannot be extended and because the constructor in the BaseEnum is private it can never be instantiated.
So for instance, if you show a list of all usernames with their types you can do something like
foreach($users as $user){
echo "<li>{$user->name}, ".UserType::toString($user->usertype)."</li>";
}
All constant can be assigned to an array using this function.
$const = get_defined_constants();
then using following function you can print the array structure
echo "<pre>";
print_r($const);
and you can see more explanation here www.sugunan.com
Warning: This way you should NOT program... ( if youre not sure what youre doing :) )
I wrote 1 row which echos constants and their numeric values by your choice of CATEGORY_
so here is the list of CATEGORY_ ERR_
foreach(get_defined_constants() as $key => $value) if(strlen($key)>5) if(substr($key, 0,5)=="ERR_") echo"<br>Found an php ERR_ constant! : ".$key."=>".$value;
And if you want just the one youre looking for by number => I created 1row function:
//input parameters: CATEGORYNAME_ , #constantNumber
function getConstantName($category,$constantNumber){foreach(get_defined_constants() as $key => $value) if(strlen($key)>strlen($category)) if(substr($key, 0,strlen($category))==$category) if($value==$constantNumber) return $key; return "No constant found.";}
So for example some info constant with number 64:
echo "NameOfConstant: ".getConstantName("INFO_",64);
will output something like: NameOfConstant: INFO_LICENSE
OK, OK, I know everything is already covered :)
But Jan Hančič asked for use case, so I'll share mine.
Aside: everyone seems to use array_flip(). Why not array_search()?
I needed it in a class that extends \Exception and is base class of small set of my concrete exceptions. Each of those concrete exception classes covers a broad exception domain and has defined several precise exception causes. Reason? I don't want to have a horde of exceptions to maintain and remember of. Also, there is exception handler set that dumps exception's guts into log file - and it's here I need to get the constant name as trying to decipher exception cause from status in quite painful.
Examples from my CLI scripts:
class AbstractException extends Exception {
public function getName() {
return array_search($this->getCode(), (new ReflectionClass($this))->getConstants());
}
}
class SyntaxException extends AbstractException {
const BAD_SYNTAX = 90;
const REQUIRED_PARAM = 91;
const REQUIRED_VALUE = 92;
const VALUE_TYPE = 93;
const VALUE_OUT_OF_BOUNDS = 94;
public function __construct ($message = "", $code = self::BAD_SYNTAX, Exception $previous = NULL) {
$script = basename($GLOBALS['argv'][0]);
echo "Invalid syntax: $message \nSee: `$script --help` for more information\n";
parent::__construct($message, $code, $previous);
}
}
// in autoload include
set_exception_handler(function(Exception $e) {
error_log(basename($GLOBALS['argv'][0]) . ';'. date('Y-m-d H:i:s') .';'. $e->getName() .';'. $e->getMessage() .';'. $e->getFile() .';'. $e->getLine() ."\n", 3, 'error.log');
exit ($e->getCode());
});
class OrderStatus
{
public const PENDING = 1;
public const PROCESSED = 2;
public static function getStatusCode($value)
{
$class = new ReflectionClass(__CLASS__);
$constants = array_flip($class->getConstants());
return $constants[$value] ?? null;
}
// OrderStatus::getStatusCode(1); // 'PENDING'
}
if you need to get the constant value on a method of the same class, you just need to use the self operator. You could use reflection if you want to use the constants on another class
class Foo {
const ERR_SOME_CONST = 6001;
function bar() {
self::ERR_SOME_CONST;
}
}