I would like to define a class constant using a concatenation of an existing constant and a string. I can't predefine it because only scalars are allowed for predefining constants, so I currently have it as part of my constructor with a defined() function checking if it is already defined. This solution works but my constant is now unnecessarily global.
Is there a way to define a class constant at runtime in php?
Thank you.
See the PHP manual on Class constants
The value must be a constant expression, not (for example) a variable, a property, a result of a mathematical operation, or a function call.
In other words, it is not possible. You could do it with runkit_constant_add but this sort of monkey patching is strongly discouraged.
Another option is to use the magic methods __get() and __set() to reject changes to certain variables. This is not so much a constant as a read-only variable (from the perspective of other classes). Something like this:
// Completely untested, just an idea
// inspired in part from the Zend_Config class in Zend Framework
class Foobar {
private $myconstant;
public function __construct($val) {
$this->myconstant = $val;
}
public function __get($name) {
// this will expose any private variables
// you may want to only allow certain ones to be exposed
return $this->$name;
}
public function __set($name) {
throw new Excpetion("Can't set read-only property");
}
}
You cannot do exactly what you want to do, per Gordon's answer. However, you can do something like this. You can only set it once:
class MyClass
{
private static $myFakeConst;
public getMyFakeConst()
{
return self::$myFakeConst;
}
public setMyFakeConst($val)
{
if (!is_null(self::$myFakeConst))
throw new Exception('Cannot change the value of myFakeConst.');
self::$myFakeConst = $val;
}
}
Related
Is there any way to use callables in PHP that won't break if you rename the underlying function (or the class, or namespace for that matter)? I find it incredibly irksome to rely on string search renaming and constant vigilance to make sure that renaming a function doesn't result in an undefined function error.
What I really want is something like C#'s nameof operator (why the hell isn't this in PHP already??), but I'm open to any solution that offers safety without impacting performance or torturing PHP. The solution being "IDE-friendly", as in static analysis friendly, is a plus, but not required.
So far I've identified a few ways of improving the situation, but all have tradeoffs:
1. Using ClassName::class
Pros:
Class can be safely renamed.
Namespace can be safely renamed.
Cons:
Function still can't be safely renamed and relies on string searching.
This doesn't solve the underlying problem, but now at least the class and namespace can be safely renamed. The only tradeoff is that it makes it harder for an IDE to figure out who this function belongs to.
namespace NS1;
class ClassOrigin {
public static function foobar() {
//...
}
}
namespace NS2;
class ClassUser {
private static function callable_user() {
$pre = "\\" . \NS1\ClassOrigin::class . "::";
$callable = $pre . 'foobar';
// ... Do stuff with the callable
}
}
2. Using variable functions
Pros:
Perfectly safe.
Cons:
Variable functions are not the same as a function. They're semantically fundamentally different and can for example be reassigned, which can be catastrophic.
Function declarations are considered expressions and cannot be assigned as a default value to a class member. This means the class needs to be initialized or needs to have a constructor. Both are undesirable when really you just want to be dealing with static functions.
namespace NS1;
class ClassOrigin {
public static $foobar;
public static function Init() {
self::$foobar = function() {
// ...
};
}
}
namespace NS2;
class ClassUser {
private static function callable_user() {
$callable = \NS1\ClassOrigin::$foobar;
// ... Do stuff with the callable
}
}
3. Using a helper function to get the function name
Pros:
Perfectly safe as long as the two functions are kept in sync.
Cons:
Needs two functions to be maintained.
This kind of convention is weird and would make some coders go "?". There are alternative conventions and possibly ways to automate the adding of the _callable() function, but it's still just pretty hacky.
namespace NS1;
class ClassOrigin {
public static function foobar_callable() {
$funcname_callable = __CLASS__ . "::" . __FUNCTION__;
$funcname = preg_replace("/_callable$/", "", $funcname_callable);
return new $funcname;
}
public static function foobar() {
//...
}
}
namespace NS2;
class ClassUser {
private static function callable_user() {
$callable = \NS1\ClassOrigin::foobar_callable();
// ... Do stuff with the callable
}
}
4. Using a parameter to conditionally return function name
Pros:
Perfectly safe.
Cons:
Pollutes the parameter list.
namespace NS1;
class ClassOrigin {
public static function foobar($return_callable = false) {
if ($return_callable) {
return __CLASS__ . "::" . __FUNCTION__;
}
else {
//...
}
}
}
namespace NS2;
class ClassUser {
private static function callable_user() {
$callable = \NS1\ClassOrigin::foobar(true);
// ... Do stuff with the callable
}
}
Other methods I've considered:
Using debug methods to get the function name. Seems like an abuse of debug functionality and probably bound to be slow, not sure if it even works out in the end.
Using the reflection library. Doesn't seem to offer a way to get the function name that isn't equally as vulnerable as just using a string directly.
Dynamically add the functions to the class and name them off of constants, thus ensuring the callables never break on rename. This is awful for too many reasons to count.
If anyone has a better idea or if I've missed some PHP functionality that could help with this, I'd love to hear it. I'm using PHPStorm and it doesn't seem like it's been able to statically determine that a string is a callable and factor that into renaming operations.
I've been looking at some code and am having a hard time working out variable declaration in php classes. Specifically it appears that the code i'm looking at doesn't declare the class variables before it uses them. Now this may be expected but I can't find any info that states that it is possible. So would you expect this:
class Example
{
public function __construct()
{
$this->data = array();
$this->var = 'something';
}
}
to work? and does this create these variables on the class instance to be used hereafter?
This works the same as a normal variable declaration would work:
$foo = 'bar'; // Created a new variable
class Foo {
function __construct() {
$this->foo = 'bar'; // Created a new variable
}
}
PHP classes are not quite the same as in other languages, where member variables need to be specified as part of the class declaration. PHP class members can be created at any time.
Having said that, you should declare the variable like public $foo = null; in the class declaration, if it's supposed to be a permanent member of the class, to clearly express the intent.
So would you expect this: (code sample) to work?
Yes. It's pretty bad practice (at least it makes my C++ skin crawl), but it wouldn't surprise me in the slightest. See example 2 in the following page for an example of using another class without declaring it beforehand. http://www.php.net/manual/en/language.oop5.basic.php It will throw an error if E_STRICT is enabled.
And does this create these variables on the class instance to be used hereafter?
Yep. Ain't PHP Fun? Coming from a C++/C# background, PHP took a while to grow on me with its very loose typing, but it has its advantages.
That's completely functional, though opinions will differ. Since the creation of the class member variables are in the constructor, they will exist in every instance of the object unless deleted.
It's conventional to declare class member variables with informative comments:
class Example
{
private $data; // array of example data
private $var; // main state variable
public function __construct()
{
$this->data = array();
$this->var = 'something';
}
}
I've had a good look round and can't seem to find an answer to this problem.
Basically I'm using the _call method to dynmically generate get and set methods, however when declaring a variable PHP's default is public. Is there anyway to declare a variable from within a class as protected?
function __call($method, $arguments) {
$prefix = strtolower(substr($method, 0, 3));
$property = strtolower(substr($method, 3));
if (empty($prefix) || empty($property)) {
return;
}
if ($prefix == "get" && isset($this->$property)) {
return $this->$property;
}
if ($prefix == "set") {
$this->$property = $arguments[0];
}
}
One option would be to have a protected array, and to set an element in that array from your magic setter.
class MyClass {
protected $_myProperties = array();
public function __get($name) {
if (isset($this->_myProperties[$name])) {
return $this->_myProperties[$name];
} else {
return null;
}
}
public function __set($name, $value) {
$this->_myProperties[$name] = $value;
}
public function __isset($name) {
return isset($this->_myProperties[$name]);
}
}
First off, I'd HIGHLY suggest not returning if the prefix or property variables are not set. It will make debugging VERY difficult. Instead, replace the return; with throw new BadMethodCallException('Method Does Not Exist: '.$method);
Second, isn't that defeating the point of protected variables? It is allowing reading and writing to all properties without any kind of validation. If you're going to do this, you might as well make them public.
I personally find $foo->bar = 'baz'; to be more readable than $foo->setBar('baz');. Not because it's "easier" to understand, but because the second is unnecessarally verbose.
Personally, I'd suggest doing a __get and __set, and adding validation. The whole point of protecting variables is for trust (so that you can trust the settings). Sure, you could use reflection or sub-classing to change them, but I usually assume that if someone goes that far, they deserve to have any unintended concequnces if they mess up a variable.
And keep in mind that if you are using any kind of magic method, you'll need to add documentation elements if you want your IDE to hint the methods/variables to you...
EDIT:
And don't forget, if you declare __get/__set methods, you can override them in the child classes. So if the child declares new variables, you can handle them there, and then call parent::__get to handle the default variables. So don't go with an array just so that you can have one method for all children. Do the validation for the members you know about, and let your children handle their own validation...
Is there anyway to declare a variable from within a class as protected?
It doesn't seem so. You can use Reflection to change the accessibility of properties, but it seems that this can only effectively be used to make things public that were not previously public.
You may wish to consider storing automatically generated properties in an an array with the proper visibility.
(There's also __get and __set, but they might not fit your needs. They'd only get called when a property is missing or inaccessible. Classes that are able to touch protected properties would bypass them.)
I've been looking at some code and am having a hard time working out variable declaration in php classes. Specifically it appears that the code i'm looking at doesn't declare the class variables before it uses them. Now this may be expected but I can't find any info that states that it is possible. So would you expect this:
class Example
{
public function __construct()
{
$this->data = array();
$this->var = 'something';
}
}
to work? and does this create these variables on the class instance to be used hereafter?
This works the same as a normal variable declaration would work:
$foo = 'bar'; // Created a new variable
class Foo {
function __construct() {
$this->foo = 'bar'; // Created a new variable
}
}
PHP classes are not quite the same as in other languages, where member variables need to be specified as part of the class declaration. PHP class members can be created at any time.
Having said that, you should declare the variable like public $foo = null; in the class declaration, if it's supposed to be a permanent member of the class, to clearly express the intent.
So would you expect this: (code sample) to work?
Yes. It's pretty bad practice (at least it makes my C++ skin crawl), but it wouldn't surprise me in the slightest. See example 2 in the following page for an example of using another class without declaring it beforehand. http://www.php.net/manual/en/language.oop5.basic.php It will throw an error if E_STRICT is enabled.
And does this create these variables on the class instance to be used hereafter?
Yep. Ain't PHP Fun? Coming from a C++/C# background, PHP took a while to grow on me with its very loose typing, but it has its advantages.
That's completely functional, though opinions will differ. Since the creation of the class member variables are in the constructor, they will exist in every instance of the object unless deleted.
It's conventional to declare class member variables with informative comments:
class Example
{
private $data; // array of example data
private $var; // main state variable
public function __construct()
{
$this->data = array();
$this->var = 'something';
}
}
First of all, I do not want to extend a class. I would ideally like to do this.
public function __construct() {
/* Set Framework Variable */
global $Five;
$this =& $Five;
}
I have a system where the variable $Five is a container class which contains other libraries. I could assign this to a local variable of Five... i.e.
public function __construct() {
/* Set Framework Variable */
global $Five;
$this->Five = $Five;
}
However, the reason why I am trying to avoid this is that function calls would be getting a little long.
$this->Five->load->library('library_name');
Its a little ugly. Far better would be.
$this->load->library('library_name');
What is the best solution for this?
I think that
$this->Five->load->library('library_name');
is going to be your best option unless you decide to have the class extend the helper class. AKA
class Something extends Helper_Class
However, this means that Helper_Class is instantiated every time you instantiate a class.
Another method would be to have a pseudo-static class that assigned all of the helper classes to class members
public function setGlobals($five)
{
$this->loader = $five->loader;
}
Then just call it
public function __construct($five)
{
someClass::setGlobals($five);
}
If $Five is a global, you could just global $Five everytime you want to use it, but putting that at the top of every function just seems like bad coding.
Also, I'd just like to do my public service announcement that Global variables are generally a bad idea, and you might want to search 'Dependency Injection' or alternative to globals. AKA
public function __construct($five);
instead of
global $five;
Globals rely on an outside variable to be present and already set, while dependency injection requests a variable that it is assuming to be an instance of the Five class.
If you are running PHP 5.1 (Thanks Gordon), you can insure the variable is an instance of the FiveClass by doing this:
public function__construct(FiveClass $five);
$this is a reference to the current instance of the class you are defining. I do not believe you can assign to it. If Five is a global you ought to be able to just do this:
$Five->load->library('library_name');
You might wanna go with some kind of implementation of the dependency injection pattern:
Dependency injection (DI) in computer
programming refers to the process of
supplying an external dependency to a
software component. It is a specific
form of inversion of control where the
concern being inverted is the process
of obtaining the needed dependency.
See also the documentation for the symfony DI container. I can highly recommend this DI container implementation if you want to improve the way you handle your 'globals'.
You could also have a read of this question on 'best ways to access global objects'.
How about making the relevant data members and methods of Five static class members? This
$this->Five->load->library('library_name');
would become this
Five::load->library('library_name');
and you wouldn't have to pass &$Five around everywhere.
You cannot overwrite $this (like e.g. in C++) but you can easily build an aggregate using __call() for method calls and __get(), __set(), __isset() for properties.
Example for __call():
class Five {
public function bar() {
echo __METHOD__, " invoked\n";
}
}
class Foo {
protected $Five = null;
public function __construct(Five $five=null) {
if ( is_object($five) ) {
$this->Five = $five;
}
}
public function __call($name, $args) {
// there's no accessible method {$name} in the call context
// let's see if there is one for the object stored in $five
// and if there is, call it.
$ctx = array($this->Five, $name);
if ( !is_null($this->Five) && is_callable($ctx) ) {
return call_user_func_array($ctx, $args);
}
else {
// ....
}
}
}
$foo = new Foo(new Five);
$foo->bar();
prints Five::bar invoked.
In my opinion the biggest draw back is that it is much harder to see "from the outside" what the object is capable of.
I'm pretty sure you can't reassign $this, as it's one of those special things that looks like a variable in PHP, but is treated slightly differently behind the scenes.
If your concerns are the semantics of your method calling getting too long, I'd make load a method call instead of an object property
$this->load()->library('library_name');
public function load()
{
return $this->Five;
}
maybe better for you will be to use PHP Magic Methods?
http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methods