Reputation: 4606
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');
?>
Upvotes: 1
Views: 102
Reputation: 97718
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]
];
}
}
Upvotes: 1
Reputation: 122439
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.
Upvotes: 0