I am defining a class which has a DateTime object as one of the properties. I want to set its default value to unix timestamp of '-1'. I also want this default value to be constant so that all objects know its value.
But I do not want to declare something like const PROPERTY_DEFAULT_DATE = '-1'; as the property will be a DateTime object and operations/functions using this default value and the property will be difficult to handle if PROPERTY_DEFAULT_DATE is not a proper DateTime object
So, can I have particular object instance of a class as constant inside another class?
The PHP manual says
The value must be a constant expression, not (for example) a variable,
a property, a result of a mathematical operation, or a function call.
Doesn't mention about this, but I think it can't be done (tried lot of variations, always got syntax errors)
If it's not possible, what alternatives do I have?
Edit : I think I need to find the solution to the problem of defining a "Default Value" to my property, which in this case happens to be a DateTime object.
What default value will you set in the __construct()? (no, not NULL please, I expect something more elegant solution must exist) Also keep in mind that it may be used in operations/functions inside the class/subclass
Update : I followed the advice here and created a private $_DEFAULT_DATE property and a getter for it. There is no setter for this property, so I can be assured that it'll not be changed. (Of course, I take care not to change it within class implementation)
Well, unfortunately, the manual is right. You cannot put an object in a constant. You can make it a property, or in your case a static function might be suited;
YourClass::getDefaultDate(); // return DateTime('-1');
It's not possible. The simplest alternative is to use a static property, but it sounds like you want to make sure this property does not change.
So in that case the only logical way to do this, is by making the static property private, and add a static function that returns the DateTime object.
However, I still don't think you want to use a singular object. If any other method requests this default object they'll be able to modify it and you might get weird results. Any request to this method (in my mind) should receive a new or cloned DateTime object.
The manual is correct: no, you can't use objects in const expressions in PHP.
You have to initialize a proper member inside a constructor if you want to use it this way. If you want it to be unalterable, with certain effort you can make it so.
from php.net
about sonstant syntax and use
Someone spoke about "dynamic" assignments to constants. What? There are no dynamic assignments to constants, runtime assignments work only with variables. Let's take the proposed example:
<?php
/**
* Constants that deal only with the database
*/
class DbConstant extends aClassConstant {
protected $host = 'localhost';
protected $user = 'user';
protected $password = 'pass';
protected $database = 'db';
protected $time;
function __construct() {
$this->time = time() + 1; // dynamic assignment
}
}
?>
Those aren't constants, those are properties of the class. Something like "this->time = time()" would even totally defy the purpose of a constant. Constants are supposed to be just that, constant values, on every execution. They are not supposed to change every time a script runs or a class is instantiated.
Conclusion: Don't try to reinvent constants as variables. If constants don't work, just use variables. Then you don't need to reinvent methods to achieve things for what is already there.
From self: you can use private static methods and use magic methods __getStatic (since it's avaliablr only from php 5.3) or use simple __get and property_exist or use Reflection. But actually I don't see the problem which need this solution. Sorry ((
Related
In PHP it is possible to get a full class name via class name resolution like this:
Example:
namespace Name\Space;
class ClassName {}
echo ClassName::class;
Output: Name\Space\ClassName
This is better than using the string Name\Space\ClassName directly in the code because code introspection especially in IDEs can find an error directly.
I wonder if there is something similar for methods of a class - this would be specifically useful for callback functions.
This is how you can basically can pass a callback:
$a = function($callback,$arg) { return $callback($arg); }
$a('getInfo',5);
Instead of passing a string here (which might change), I would prefer to do something like this:
$a(MyClass::class::getInfo,5);
With I "go to declaration" click in the IDE I could go directly to getInfo plus I see errors in case with method does not exist anymore. Is there a way to achieve what I want to do here?
In fact, you work with callable type. And PHP allows setting method/function name only as a string. But if you use classes and objects you will have a different way to set callback. For example:
$a = function($callback, $arg) {
return call_user_func($callback, $arg));
}
// call a static method of the class with
// using fullname space and method name as a string
$a('Name\Space\MyClass::getInfo',5);
// call a static method of the class
// with using ::class
$a([MyClass::class, 'getInfo'], 5);
// call a method of an object
$myObject = new MyClass();
$a([$myOject, 'getInfo'], 5);
Three possibilities.
(1)
echo `__CLASS__`;
...returns namespace\classname as a string.
(2)
If you're trying to get the namespace\classname from another class, i.e., not the one where you're currently executing code, then I would suggest setting a public property inside each class such as:
public static $classname = __CLASS__;
which you could then access from anywhere as:
ClassName::$classname
Put it in each of your classes. Always use the same property name.
(3)
Have you considered the PHP function debug_backtrace() which returns a call stack with the most recent call at index = 0;
So, if:
$caller = debug_backtrace();
Then, $caller[0]['class'] contains the fully qualified class name, including any namespace, at the point where you called debug_backtrace().
I'm guessing that #2 is the solution that will work for you.
Just thought of a 4th possibility that doesn't depend on you adding any code to each class. Might add some overhead though, but so does my 3rd solution above.
(4)
$declared_classes = get_declared_classes();
This lists all of the classes currently declared within the PHP scope as fully qualified namespace\classname. You could search the returned array for partial string matches within the array and return the whole namespace\classname string.
One thing to keep in mind. You might have duplicates if different namespaces have same-named classes.
I've added this as a comment somewhere else but figured it might warrant an actual answer to this question. If you use:
$callback = [MyClass::class, 'myMethod'];
Then at least one IDE (PhpStorm) will recognize this as the callable that it is, allow you to navigate to it, mention it in "show usages" and automatically change it when it is renamed through a refactor. I use this in my code if, for instance, I reference a method in a test:
$this->mock(MyClass::class, function(MockInterface $mock) {
$mock->shouldReceive([MyClass:class, 'myMethod'][1])->andReturn(10);
});
Not the cleanest syntax, but it's workable.
I was looking through the PHP documentation and saw several comments where a variable was initialized outside of a class's constructor, similar to the following:
classMyClass {
private $count = 0;
public function __construct() {
//Do stuff
}
}
In PHP Objects, Patterns, and Practice, the author recommends using constructs only for the initialization of properties, deferring any heavy lifting or complex logic to specialized methods. This tutorial (a quick example that I found on Google) also recommends using constructors to initialize properties: http://www.killerphp.com/tutorials/object-oriented-php/php-objects-page-3.php.
Why would you want to initialize a variable outside the constructor? Is this just sloppy coding, or is there a reason to do something like this? I have to say that until recently, I initialized default values outside the constructor, and there doesn't seem to be any programmatic advantage of one way over the other.
When you initialize a variable outside of the constructor, it must be initialized as a constant. You can't do any operation to initialize it. Thus, the initial value of that member is actually a part of the class signature.
For example, this is invalid:
private $var = $othervar;
private $var = func();
You could do it in the constructor as well, but it would be a bit more verbose and add some clutter to the constructor unless there was some sort of logic going on.
More a comment than an answer, but please elaborate here a little:
Since it is recommended to use constructors for property initialization only,
Who says this and why? I assume the only relates to something else than property definitions with default values.
An answer part:
By default in PHP variables do not need to be defined because variables are then defined when first accessed in a write context. All variables, including undefined ones contain NULL (Demo):
class A {}
$a = new A;
var_dump($a->property); # NULL
Introducing class variables (properties) PHP then allowed to actually define variables. Those still return NULL by default, but they are defined (Demo):
class A {
public $property;
}
$a = new A;
var_dump($a->property); # NULL
In the next step of the evolution, this language construct also allows to specify a constant expression. That is constant because definition is compile-time (not run-time as the when the constructor is invoked). An example (Demo):
class A {
public $property = 'hello';
}
$a = new A;
var_dump($a->property); # string(5) "hello"
As this is compile- but your constructor run-time, I find it hard to compare both feature with another. Also it's not clear why you say that initializing via the constructor is recommended.
Far from sloppy... it's good programming practice. As you would also do in Java/C++, it just sets them up, and then you can do any initialisation in the constructor - usually to sent them to non-defaults as such.
I've been wondering if a class property is instantiated and used only in one class method should it be a class property at all or should it just be a local variable accessible to that class method only?
For example, should I keep a variable only used in one method as a local variable like this:
class myClass
{
public function myMethod()
{
$_myVariableUsedOnlyOnce = "Hello World";
echo $_myVariableUsedOnlyOnce;
}
}
Or should I make the variable a private class property like this:
class myClass
{
private $_myVariableUsedOnlyOnce;
public function myMethod()
{
$this->_myVariableUsedOnlyOnce = "Hello World";
echo $this->_myVariableUsedOnlyOnce;
}
}
Which approach "smells"? What are the benefits to making all method variables class properties other than when I need to print_r() the entire object for debugging purposes?
Thanks
If you need it to have persistence across function calls, a class property would be best so that it moves around as the object does. You also might want to use it for other reasons in future in other functions. However, it does add overhead.
Generally, the class should have some real-world analogue, so if your variable corresponds to something that makes sense e.g. a person class has a $height, then it belongs as a class property. Otherwise, if it's just a part of the internal calculations of a method, then it doesn't really belong attached to the class e.g. a person does not have a $shoelaceIterator or whatever.
I'd argue that a confusing object design would be more of a smell than a potentially small memory overhead (although this depends on how big the variable is).
These local variables are not properties of your object.
They are not defining your object, then they should not be declared as private member.
First I would ask if you really need the variable/property at all if you are only using it once. As for which one "smells", a property is stored in memory for the entire life of the object whereas the variable is only in memory until the method finishes executing.
If you don't need a variable outside the method, it should not be any property of the class. Moreover, accessing local variables is faster.
In a pure design approach I would suggest you to make your choice according to what the attribute/property is supposed to model.
In pure performance terms, having one static attribute is better because memory space won't be allocate with each instance of the class.
Can I define a class constant inside the class constructor function ?
(based on certain conditions)
That goes against the idea of class constants - they should not be dependent on a specific instance. You should use a variable instead.
However, if you insist on doing this, are very adventurous and can install PHP extensions, you can have a look at the runkit extension that allows to modify classes and their constants at runtime. See this doc: http://www.php.net/manual/en/function.runkit-constant-add.php
I don't think you can.
It wouldn't make sense, either - a class constant can be used in a static context, where there is no constructor in the first place.
You'll have to use a variable instead - that's what they're there for.
Try look here:
http://php.net/manual/en/language.oop5.constants.php
http://php.net/manual/en/language.oop5.static.php
Hope this helps.
As far as standard instance constructors go, there is no way to do this, and as others have pointed out, it wouldn't make sense. These constructors are called per created object instance, at the point they are created. There is no guarantee this constructor would get called before some code tried to access the constant. It also doesn't make sense in that the code would get called over and over again each time a new instance was constructed, whereas a const should only get set once.
It would be nice if PHP either offered some kind of static constructor that let you set the value one time for uninitialized constants, or allowed more types of expressions when defining constants. But these are not currently features of PHP. In 2015 an RFC was made that proposed adding static class constructors, but it is, at the time of me writing this answer, still in the draft status, and has not been modified since 2017.
I think the best alternative for now is to not use constants in this kind of scenario, and instead use static methods that return the value you want. This is very simple in that it only uses the PHP language features as is (not requiring any special extensions), these static methods can be called in the standard way, and you don't need to hack the autoloading process to call some kind of initializer function that sets static variables. The method might need to rely on private static variables in order to make sure the same instance is returned every time, if an object instance is being returned. You would need to write the implementation of this method to be constant like in the sense that it will always return the same thing, but takes advantage of being able to do things you can't do with a constant, like return on object instance or rely on complex expressions or function calls. Here is an example:
final class User
{
/** #var DefinitelyPositiveInt|null */ private static $usernameMaxLength;
public static function getUsernameMaxLengthConst(): DefinitelyPositiveInt
{
if ($usernameMaxLength === null) {
$usernameMaxLength = new DefinitelyPositiveInt(40);
}
return $usernameMaxLength;
}
}
$usernameInput.maxLength = User::getUsernameMaxLengthConst();
This is still not a perfect solution because it relies on the programmer to write these in a constant like way when that is desired (always returning the same value). Also, I don't like that the best place to document the fact that it is a const is in the method name, thus making it even longer to call. I also don't like that you now have to call it as a method instead of just accessing a property, which would be syntactically nicer.
This example is essentially an implementation of a singleton, but sometimes the purpose of a singleton is to be a constant rather than just a singleton. What I mean is, you might want the instance to always exist, and it might be an immutable type (none of the properties are public or mutable, only having methods that return new objects/values).
I am sorry to break it to you but it is not possible in vanilla PHP.
I am not very sure about frameworks or extensions but I am sure that it is not possible in vanilla PHP.
I recommend you to use variables instead.
You still can't, but maybe some of these (progressively weirder) ideas (just ideas, not true solutions) will work for you:
(1) You could use a private property, with a public getter method. The property cannot be modified outside the class, such as constants, but unfortunately it is accessed as a method, not as a constant, so the syntax is not the same.
class aClass{
private $const;
function __construct($const){
$this->const=$const;
}
function const(){
return $this->const;
}
}
$var1=new aClass(1);
echo $var1->const(); //Prints 1
(2) If you really want this value to be accessed as constant from outside, you can use define () inside the constructor. Unfortunately it doesn't get tied to the class or object name (as it do when you use const, using for example myClass::myConst). Furthermore, it only works if you create a single instance of the class. The second object you create is going to throw an error for redefining the constant, because is untied.
class otherClass{
function __construct($const){
define('_CONST',$const);
}
function const(){
return _CONST;
}
}
$var2=new otherClass('2');
echo $var2->const(); //Prints 2
echo _CONST; //Prints 2
#$var3=new aClass('3'); //Notice: Constant _CONST already defined
echo _CONST; //Still prints 2!
(3) Perhaps that last problem can be solved by giving variable names to the constants, related to the object to which they belong. This may be a bit weird... but maybe it works for someone.
class onemoreClass{
private $name;
function __construct($const,$name){
$this->name=$name;
$constname=$this->name."_CONST";
define($constname,$const);
}
function const(){
return constant($this->name.'_CONST');
}
}
$name='var4';
$$name=new onemoreClass(4,$name);
echo $var4->const(); //Prints 4
echo var4_CONST; //Prints 4
$name='var5';
$$name=new onemoreClass(5,$name);
echo $var5->const(); //Prints 5
echo var5_CONST; //Prints 5
I've had a good read of the PHP specs on overloading, and most of the examples appear to be intended to simply allow defining of custom fields (similar to stdClass).
But what about the private fields defined in my class? How should these be retrieved/assigned? I could do a switch on possible values and act on certain values:
class A
{
private $name;
private $age;
public function __get( $var )
{
switch ( $var )
{
case 'name':
return $this->name;
break;
case 'age':
return $this->age+10; // just to do something different ;)
break;
default:
break;
}
}
}
Is this the best method, or is there another generally accepted best practice? (I don't know if it's possible to loop class variables inside a class, but regardless that's not appropriate in most situations since you don't want to return everything.)
I make extensive use of __get() and __set() AND I have my properties declared public. I wanted the best of both worlds, so my IDE could list public properties for me when I type the name of a class instance. How did I get the PHP interceptor methods to work, even though the properties are public and visible outside the class?unset() the properties I want __get() and __set() to be used for in the class __construct().Figuring this method out took a couple evenings of frustration :-)
This would effectively make them public, and usually this gains you nothing over simply using public properties (but you pay performance penalty and have to write more code, risk bugs).
Use that only if you have existing code that uses public property and you suddenly need getter/setter for it (like your example with age).
If you need to execute some code when property is read/written (e.g. make it read-only or lazily fetch from the database), then you should use normal getters/setters (getAge()/setAge()).
I don't see many people using them, but here is what I think.
For retrieving private variables, you probably want to give a list of defined variables that are able to be accessed by the outside. Then, check to see if the requested variable is in that array. If it is, allow it to be changed/gotten. If not, either error or return null.
Honestly, the implementations for this are really case-by-case. If you had a user class that you stored all the info in a single array, you could have $user->name be pulled from $user->account['name'], or something like that.
Also, in a more case-by-case, you could do some modification before giving them values, like hashing a password.