I have a class defined which has several constants defined through `const FIRST = 'something';
I have instantiated the class as $class = new MyClass()
then I have another class that takes a MyClass instance as one of it's constructors parameters and stores it as $this->model = $myClassInstance;
This works fine.
But I am wondering how I can access the constants from that instance?
I tried case $this->model::STATE_PROCESSING but my IDE tells me
Incorrect access to static class member.
and PHP tells me
unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM) in ...
I know I can do MyClass::STATE_PROCESSING but I am wondering if there is a way to get them based off the instance?
Seems like your on an older version of php? PHP 5.3 allows the access of constants in the manner you describe... However, this is how you can do it without that inherent ability:
class ThisClass
{
const FIRST = 'hey';
public function getFIRST()
{
return self::FIRST;
}
}
class ThatClass
{
private $model;
public function setModel(ThisClass $model)
{
$this->model = $model;
}
public function getModel()
{
return $this->model;
}
public function Hailwood()
{
$test = $this->model;
return $test::FIRST;
}
}
$ThisObject = new ThisClass();
echo $ThisObject ->getFIRST(); //returns: hey
echo $ThisObject ::FIRST; //returns: hey; PHP >= 5.3
// Edit: Based on OP's comments
$ThatObject= new ThatClass();
$ThatObject->setModel($Class);
echo $ThatObject->getModel()->getFIRST(); //returns: hey
echo $ThatObject->Hailwood(); //returns: hey
Basically, were creating a 'getter' function to access the constant. The same way you would to externally access private variables.
See the OOP Class Constants Docs: http://php.net/manual/en/language.oop5.constants.php
Related
Here's the code (didn't include namespaces, routing):
class OneController extends Controller{
public $variable = "whatever";
public function changeVariableAction(){
$this->variable = "whenever";
// any code...
$this->redirectToRoute("class_two_route_name");
}
}
use AppBundle\Controller\OneController;
class Two{
public function otherFunctionAction(){
$reference = new One();
return new Response($reference->variable);
}
}
Why do I see "whatever" instead "whenever"? I know there is no line in the code executing changeVariableAction() but it is being executed when sb enters the route matching this action in class One ???
EDIT:
When I write the scheme outside SF3 I'm OK.
class One{
public $variable = "whatever";
public function changeVariable(){
$this->variable = "whenever";
}
}
class Two{
public function otherFunction(){
$reference = new One();
$reference->changeVariable();
echo $reference->variable;
}
}
$reference2 = new Two();
$reference2->otherFunction();
You are seeing "Whatever" instead of "Whenever" because of this line:
new One();
By calling "new One();" you are creating a new instance of the class "OneController" thus, it will set its default value "whatever" as the function "changeVariableAction" is not being called in your new instance $reference.
After research I can see that in SF (as it is a framework) we don't treat Action functions as typical functions (it's sth about http etc.) so we cannot execute them in another class. What's more, the whole code inside Action function doesn't influence the code outside the Action function. The only way to get new property value is to send them via argument in url (I don't think we want that) or send to db and retrieve it from database in another class.
Here's the proof:
class FirstController extends Controller{
public $variable = "whatever";
/**
* #Route("/page")
*/
public function firstAction(){
$this->variable = "whenever";
return $this->redirectToRoute("path");
}
}
class SecondController{
/**
* #Route("/page/page2", name = "path")
*/
public function secondAction(){
$reference = new FirstController();
$reference->firstAction();
return new Response($reference->variable);
}
}
This code gives an error: Call to a member function get() on null.
When I delete line $reference->firstAction(); there is no error and "whatever" shows up (so the original).
This question already has an answer here:
what means new static? [duplicate]
(1 answer)
Closed 7 days ago.
I don't understand what static() does here. Static can be used as Scope Definer. A function or a variable can be static. But what AWS is defining through new static() here?
private static function createFromEnvironment($config)
{
// Get key and secret from ENV variables
$envKey = self::getEnvVar(self::ENV_KEY);
if (!($envSecret = self::getEnvVar(self::ENV_SECRET))) {
// Use AWS_SECRET_ACCESS_KEY if AWS_SECRET_KEY was not set.
$envSecret = self::getEnvVar(self::ENV_SECRET_ACCESS_KEY);
}
// Use credentials from the environment variables if available
if ($envKey && $envSecret) {
return new static($envKey, $envSecret);
}
// Use credentials from the ini file in HOME directory if available
$home = self::getHomeDir();
if ($home && file_exists("{$home}/.aws/credentials")) {
return self::fromIni($config[Options::PROFILE], "{$home}/.aws/credentials");
}
// Use instance profile credentials (available on EC2 instances)
return new RefreshableInstanceProfileCredentials(
new static('', '', '', 1),
$config[Options::CREDENTIALS_CLIENT]
);
}
The static keyword is used to support "late static binding". It is used when you have a class that extends another, and where you are a calling static method on the child class that is actually defined in the parent class. Take this code as an example:
class Greeter {
public static function greet() {
echo self::getGreeting();
}
public static function getGreeting() {
return 'Hello';
}
}
class SpanishGreeter extends Greeter {
public static function getGreeting() {
return 'Hola';
}
}
SpanishGreeter::greet();
You might initially expect that this would echo "Hola", but it actually echoes "Hello". This is because the self keyword used in the greet() method will cause the getGreeting() method of self, the parent Greeter class, to be called, instead of the class you actually referenced. If you changed the code to say echo static::getGreeting();, then "Hola" would be echoed, because the static keyword binds that getGreeting() method call to the class that you actually referenced.
Similarly, if you called new self() within the greet() method. This would return a instance of Greeter, but new static() would return an instance of SpanishGreeter.
The usage in the AWS SDK that you've referenced is relying on this late static binding behavior. Though in this specific part of the code, I don't think it is actually needed/significant.
I'm a bit confused on how constructors work in PHP.
I have a class with a constructor which gets called when I instantiate a new object.
$foo = new Foo($args);
__construct($params) is called in the class Foo and it executes the appropriate initialization code.
However when I use the class to call a static function, the constructor is called again.
$bar = Foo::some_function(); //runs the constructor from Foo
This causes the constructor to execute, running the object initialization code that I intended only for when I create a new Foo object.
Am I missing the point of how constructors work? Or is there a way to prevent __construct() from executing when I use the class to make static function calls?
Should I use a "factory" function instead to do the object initialization? If so, what's the point of the constructor then?
::EDIT::
I have a form where users can upload photos to an album (create_photo.php) and an area where they can view the album (view_photos.php). Upon form submit:
$photo = new Photo($_FILES['photo'], $_POST['arg1'], ect..);
The Photo constructor creates and saves the photo. However in view_photo.php, when I call:
$photo = Photo::find_by_id($_POST['id']) //user-defined function to query database
This is causing Photo's constructor to run!
I see nothing that replicates your question.
See Demo: http://codepad.org/h2TMPYUV
Code:
class Foo {
function __construct(){
echo 'hi!';
}
static function bar(){
return 'there';
}
}
echo Foo::bar(); //output: "there"
Assumption
PHP 5.x
Different goals, different path
create a new instance of a class (object)
class myClassA
{
public $lv;
public function __construct($par)
{
echo "Inside the constructor\n";
$this->lv = $par;
}
}
$a = new myClassA(11);
$b = new myClassA(63);
because we create a new object PHP calls:
__construct($par);
of the new object, so:
$a->lv == 11
$b->lv == 63
use a function of a class
class myClassB
{
public static $sv;
public static function psf($par)
{
self::$sv = $par;
}
}
myClassB::psf("Hello!");
$rf = &myClassB::$sv;
myClassB::psf("Hi.");
now $rf == "Hi."
function or variabiles must defined static to be accessed by ::, no object is created calling "psf", the "class variable" sv has only 1 instance inside the class.
use a singleton created by a Factory (myClassA is above)
class myClassC
{
private static $singleton;
public static function getInstance($par){
if(is_null(self::$singleton)){
self::$singleton = new myClassA($par);
}
return self::$singleton;
}
}
$g = myClassC::getInstance("gino");
echo "got G\n";
$p = myClassC::getInstance("pino");
echo "got P\n";
Using the factory (getInstance) the first time we construct a new object having $par set to gino.
Using the factory the second time $singleton has already a value that we return. No new object is created (no __construct is called, less memory & cpu is used).
The value of course is an object instanceOf myClassA and don't forget:
myClassC::$singleton->lv == "gino"
Pay attention to singletons:
What is so bad about singletons?
http://www.youtube.com/watch?v=-FRm3VPhseI
By my answer I don't want promote/demote singleton. Simply from the words in the question, I made this calc:
"static"+"__construct"="singleton"!
Here is my workaround:
I put method construct() in static class. Notice, it is different than __construct() which I use in regular classes.
Each class is in own file, so I lazy load that file on first use of class. This gives me event of first use of class.
spl_autoload_register(function($class) {
include_once './' . $class . '.php';
if (method_exists($class, 'construct')) {
$class::construct();
}
});
I define class properties as array in a static method and call them via the method. I'm not sure if it's the best solution or not but works great.
Example:
class Foo
{
private static construct_method()
{
return [
'one' => 1,
'two' => 2
];
}
public static any_method()
{
return self::construct_method()['one'] + self::construct_method()['two'];
}
}
echo Foo::any_method(); // 3
I want to create an instance of a class and call a method on that instance, in a single line of code.
PHP won't allow calling a method on a regular constructor:
new Foo()->set_sth(); // Outputs an error.
So I'm using, if I can call it that, a static constructor:
Foo::construct()->set_sth();
Here's my question:
Is using static constructors like that considered a good practice and if yes, how would you recommend naming the methods for these static constructors?
I've been hesitating over the following options:
Foo::construct();
Foo::create();
Foo::factory()
Foo::Foo();
constructor::Foo();
Static constructors (or "named constructors") are only beneficial to prove an intention, as #koen says.
Since 5.4 though, someting called "dereferencing" appeared, which permits you to inline class instantiation directly with a method call.
(new MyClass($arg1))->doSomething(); // works with newer versions of php
So, static constructors are only useful if you have multiple ways to instantiate your objects.
If you have only one (always the same type of arguments and number of args), there is no need for static constructors.
But if you have multiple ways of instantiations, then static constructors are very useful, as it avoids to pollute your main constructor with useless argument checking, weakening languages constraints.
Example:
<?php
class Duration
{
private $start;
private $end;
// or public depending if you still want to allow direct instantiation
private function __construct($startTimeStamp = null, $endTimestamp = null)
{
$this->start = $startTimestamp;
$this->end = $endTimestamp;
}
public static function fromDateTime(\DateTime $start, \DateTime $end)
{
return new self($start->format('U'), $end->format('U'));
}
public static function oneDayStartingToday()
{
$day = new self;
$day->start = time();
$day->end = (new \DateTimeImmutable)->modify('+1 day')->format('U');
return $day;
}
}
As you can see in oneDayStartingToday, the static method can access private fields of the instance! Crazy isn't it ? :)
For a better explanation, see http://verraes.net/2014/06/named-constructors-in-php/
The naming of any method should be with intention revealing names. I can't tell what 'Foo::factory' does. Try to build to a higher level language:
User::with100StartingPoints();
This would be the same as:
$user = new User();
$user->setPointsTo(100);
You could also easily test whether User::with100StartingPoints() is equal to this.
If you don't need a reference to the newly constructed Foo, why don't you simply make set_sth a static function (and have it create a new Foo internally if required)?
If you do need to get hold of the reference, how would you do it? return $this in set_sth? But then set_sth can be made into a factory function anyway.
The only situation I can think of is if you want to call chainable methods (like in a fluent interface) on a newly constructed instance all in one expression. Is that what you are trying to do?
Anyway, you can use a general-purpose factory function for all types of objects, e.g.
function create_new($type) {
return new $type;
}
create_new('Foo')->set_sth();
It's probably not quite a best practice, but you could use the fact that functions and classes have two different namespaces : you can have a function that have the same name as a class.
This allows one to write this kind of code, for example :
function MyClass() {
return new MyClass();
}
class MyClass {
public function __construct() {
$this->a = "plop";
}
public function test() {
echo $this->a;
}
protected $a;
}
Note that I have defined a function called MyClass, and a class with the same name.
Then, you can write this :
MyClass()->test();
Which will work perfectly, and not get you any error -- here, you'll get the following output :
plop
Addition to Jon's answer: To allow constructor arguments use the following:
function create($type) {
$args = func_get_args();
$reflect = new ReflectionClass(array_shift($args));
return $reflect->newInstanceArgs($args);
}
create('Foo', 'some', 'args')->bar();
Documentation: ReflectionClass->newInstanceArgs
These are called creation methods, and I typically name them createXXX() such as createById() or createEmptyCatalog(). Not only do they provide a nice way to reveal the different intentions of an object's constructors, but they enable immediate method chaining in a fluent interface.
echo Html_Img::createStatic('/images/missing-image.jpg')
->setSize(60, 90)
->setTitle('No image for this article')
->setClass('article-thumbnail');
Propel uses a static method "create". I'd go with that. This method makes the code easier to test rather than just using static methods to perform business logic.
<?php
class MyClass
{
public static function create()
{
return new MyClass();
}
public function myMethod()
{
}
}
Besides, you can also pass parameters to the constructor. For instance:
<?php
class MyClass
{
public function __construct($param1, $param2)
{
//initialization using params
}
public static function create($param1, $param2)
{
return new MyClass($param1, $param2); // return new self($param1, $param2); alternative ;)
}
public function myMethod()
{
}
}
In either case, you'd be able to invoke myMethod right after the create method
<?php
MyClass::create()->myMethod();
// or
MyClass::create($param1, $param2)->myMethod();
A bit late to the party but I think this might help.
class MyClass
{
function __construct() {
// constructor initializations here
}
public static myMethod($set = null) {
// if myclass is not instantiated
if (is_null($set)) {
// return new instance
$d = new MyClass();
return $d->Up('s');
} else {
// myclass is instantiated
// my method code goes here
}
}
}
this can then be used as
$result = MyClass::myMethod();
optional parameters can be passed through either the __constructor or myMethod.
This is my first post and I hope I got the gimmicks right
This question already has answers here:
PHP Readonly Properties?
(7 answers)
Closed last year.
When trying to change it,throw an exception.
I suppose a solution, for class properties, would be to :
not define a property with the name that interests you
use the magic __get method to access that property, using the "fake" name
define the __set method so it throws an exception when trying to set that property.
See Overloading, for more informations on magic methods.
For variables, I don't think it's possible to have a read-only variable for which PHP will throw an exception when you're trying to write to it.
For instance, consider this little class :
class MyClass {
protected $_data = array(
'myVar' => 'test'
);
public function __get($name) {
if (isset($this->_data[$name])) {
return $this->_data[$name];
} else {
// non-existant property
// => up to you to decide what to do
}
}
public function __set($name, $value) {
if ($name === 'myVar') {
throw new Exception("not allowed : $name");
} else {
// => up to you to decide what to do
}
}
}
Instanciating the class and trying to read the property :
$a = new MyClass();
echo $a->myVar . '<br />';
Will get you the expected output :
test
While trying to write to the property :
$a->myVar = 10;
Will get you an Exception :
Exception: not allowed : myVar in /.../temp.php on line 19
class test {
const CANT_CHANGE_ME = 1;
}
and you refer it as test::CANT_CHANGE_ME
Use a constant. Keyword const
The short answer is you can't create a read-only object member variable in PHP.
In fact, most object-oriented languages consider it poor form to expose member variables publicly anyway... (C# being the big, ugly exception with its property-constructs).
If you want a class variable, use the const keyword:
class MyClass {
public const myVariable = 'x';
}
This variable can be accessed:
echo MyClass::myVariable;
This variable will exist in exactly one version regardless of how many different objects of type MyClass you create, and in most object-oriented scenarios it has little to no use.
If, however, you want a read-only variable that can have different values per object, you should use a private member variable and an accessor method (a k a getter):
class MyClass {
private $myVariable;
public function getMyVariable() {
return $this->myVariable;
}
public function __construct($myVar) {
$this->myVariable = $myVar;
}
}
The variable is set in the constructor, and it's being made read-only by not having a setter. But each instance of MyClass can have its own value for myVariable.
$a = new MyClass(1);
$b = new MyClass(2);
echo $a->getMyVariable(); // 1
echo $b->getMyVariable(); // 2
$a->setMyVariable(3); // causes an error - the method doesn't exist
$a->myVariable = 3; // also error - the variable is private
I made another version that uses #readonly in the docblock instead of private $r_propname. This still doesn't stop the declaring class from setting the property, but will work for public readonly access.
Sample Class:
class Person {
use Readonly;
/**
* #readonly
*/
protected $name;
protected $phoneNumber;
public function __construct($name){
$this->name = $name;
$this->phoneNumber = '123-555-1234';
}
}
The ReadOnly trait
trait Readonly {
public function readonly_getProperty($prop){
if (!property_exists($this,$prop)){
//pretty close to the standard error if a protected property is accessed from a public scope
trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
}
$refProp = new \ReflectionProperty($this, $prop);
$docblock = $refProp->getDocComment();
// a * followed by any number of spaces, followed by #readonly
$allow_read = preg_match('/\*\s*\#readonly/', $docblock);
if ($allow_read){
$actual = $this->$prop;
return $actual;
}
throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
}
public function __get($prop){
return $this->readonly_getProperty($prop);
}
}
See the source code & test on my gitlab
I cooked up a version, too, using a trait.
Though in this case, the property can still be set by its declaring class.
Declare a class like:
class Person {
use Readonly;
protected $name;
//simply declaring this means "the 'name' property can be read by anyone"
private $r_name;
}
And this is the trait I made:
trait Readonly {
public function readonly_getProperty($prop){
if (!property_exists($this,$prop)){
//pretty close to the standard error if a protected property is accessed from a public scope
// throw new \Error("Property '{$prop}' on class '".get_class($this)."' does not exist");
trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
}
$allow_read = property_exists($this, 'r_'.$prop );
if ($allow_read){
$actual = $this->$prop;
return $actual;
}
throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
}
public function __get($prop){
return $this->readonly_getProperty($prop);
}
}
See the source code & test on my gitlab
I know this is an old question, but PASCAL's answer really helped me and I wanted to add to it a bit.
__get() fires not only on nonexistent properties, but "inaccessible" ones as well, e.g. protected ones. This makes it easy to make read-only properties!
class MyClass {
protected $this;
protected $that;
protected $theOther;
public function __get( $name ) {
if ( isset( $this->$name ) ) {
return $this->$name;
} else {
throw new Exception( "Call to nonexistent '$name' property of MyClass class" );
return false;
}
}
public function __set( $name ) {
if ( isset( $this->$name ) ) {
throw new Exception( "Tried to set nonexistent '$name' property of MyClass class" );
return false;
} else {
throw new Exception( "Tried to set read-only '$name' property of MyClass class" );
return false;
}
}
}