Is it normal to not defined variable in class properties, but indicate at once in constructor ?
I just learn OOP, and found that example:
class Foo {
public function __construct($name) {
$this->_name = $name;
}
}
and it works, i can use this variable
$objFoo = new Foo('Fido');
echo $objFoo->_name; //output 'Fido'
But in class properties, there has no defined variable $_name, like public $_name in start of class.
So if we dont define it directly, it will be created automatically throught constructor ? And is it real to set scope for variable that way, protected, private ?
In PHP, this is called "object overloading", you can add/set new properties on (almost) any instance on the fly. However, you shouldn't do this. These properties make your code more error prone, less maintainable and overloaded properties that are added are slower: details here.
Pre-declaring properties allows for better documentation, your IDE's auto-completion function is actually worth something then, and you don't get in the habit of relying on something that doesn't always work. I tend to write my data models along these lines:
class DataModel
{
/**
* #var int
*/
protected $id = null;
/**
* #var string
*/
protected $name = null;
/**
* #var \DateTime
*/
protected $birthday = null;
public function __construct(array $data)
{}//set values
public function __set($name, $value)
{
throw new \BadMethodCallException('Do not attempt overloading, use getters and setter');
}
}
I define getters and setters for all of the properties, to ensure type safety, and validation can be done. I expressly disable overloading because of the risk of typo's, and difficult debugging. I often find myself disabling __sleep, __serialize and all those magic methods for pretty much the same reason.
Creating object members on the fly, meaning just assigning a value to it without declaring the member, is not limited to the constructor method. It can be done from inside of every method in the class or even from outside of the class, like this:
$c = new stdClass();
$c->foo = "bar";
While there are still a few use cases for this, I suggest to declare every class member explicitly in the head of the class. Also you will want in most cases to declare class members as protected or even private which requires an explicit declaration anyway.
Related
Say I have a very simple CRUD system in PHP to manage a database. Say it holds a list of products. Using Doctrine ORM, I'd like to query the database and view/edit/add records. According to the Getting Started manual,
When creating entity classes, all of the fields should be protected or
private (not public), with getter and setter methods for each one
(except $id). The use of mutators allows Doctrine to hook into calls
which manipulate the entities in ways that it could not if you just
directly set the values with entity#field = foo;
This is the sample provided:
// src/Product.php
class Product
{
/**
* #var int
*/
protected $id;
/**
* #var string
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
// Recording a new title
$product->setName("My new name");
$db->persist($product);
$db->flush();
// Echoing the title
echo $product->getName();
However, this seems overly complicated. Say I don't have a need to hook into calls to manipulate entities, as explained in the manual. I can make this code much shorter and do this:
// src/Product.php
class Product
{
/**
* #var int
*/
public $id;
/**
* #var string
*/
public $name;
}
This would allow things like this:
$product = $db->getRepository('Product')->find(1);
// Recording a new name
$product->name = "My new title";
$db->persist($product);
$db->flush();
// Echoing the title
echo $product->name;
The advantages are:
Always using the exact same name
No extra setters and getters in the entity class
What is the disadvantage of using this? Am I taking certain risks here?
Say I don't have a need to hook into calls to manipulate entities, as explained in the manual.
You do not need to hook into these calls, but Doctrine does. Doctrine internally overrides these getters and setters to implement an ORM as transparently as possible.
Yes, it is overly complicated, but that is the PHP language at fault and not Doctrine. There is nothing inherently wrong with using public properties, it is just that the PHP language does not allow creating custom getters/setters the way some other languages do (such as Kotlin, C#).
What does Doctrine do exactly? It generates so-called Proxy objects to implement lazy loading. These objects extend your class and override certain methods.
For your code:
// src/Product.php
class Product
{
/**
* #var int
*/
protected $id;
/**
* #var string
*/
protected $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
Doctrine might generate:
class Product extends \YourNamespace\Product implements \Doctrine\ORM\Proxy\Proxy
{
// ... Lots of generated methods ...
public function getId(): int
{
if ($this->__isInitialized__ === false) {
return (int) parent::getId();
}
$this->__initializer__ && $this->__initializer__->__invoke($this, 'getId', []);
return parent::getId();
}
public function getName(): string
{
$this->__initializer__ && $this->__initializer__->__invoke($this, 'getName', []);
return parent::getName();
}
// ... More generated methods ...
}
(What this does exactly is not the point, the point is that Doctrine MUST be able to override getters and setters to implement an ORM)
To see for yourself, inspect the generated Proxy objects in the proxy directory of Doctrine. See the Advanced Configuration section for more info on configuring the proxy directory. Also see Working with Objects for more info on Proxy objects and how Doctrine utilizes them.
Why not use public entity classes in Doctrine?
Actually what you are asking is:
Where does this getters/setters come from?
It comes from the Encapsulation in OOP.
so, what is encapsulation?
It’s the way we define the visibility of our properties and methods. When you’re creating classes, you have to ask yourself what properties and methods can be accessed outside of the class.
Let’s say we had a property named id. If a class extends your class, is it allowed to manipulate and access id directly? What if someone creates an instances of your class? Are they allowed to manipulate and access id?
NO! They should not be able to reach the id attribute directly.
Why not?
Example: What happens if you only accept Invoice Ids which start with "IX" and other classes have access to your class and they set the invoice_id directly?
What happens if they accidentally set the invoice_id to "XX12345"?
of course, you cannot do anything (like validating the input), because you are using public properties and no setters.
but if you had a setter method in your class, you could just do
private $invoiceId;
public function setInvoiceId($invoiceId)
{
if (is_null($invoiceId) || preg_match("#^IX(.*)$#i", $invoiceId) === 0)
return false;
$this->invoiceId = $invoiceId;
return $this;
}
I hope it was clear enought. I will try to extend the answer
What I'm trying to do is bitwise flagging when using setPARAM().
What I know is magic function __get() or __set() being called when you refering protected variables and its also not a good practice.
So,
// This is an example for what I did.
class boo {
// #var string name ;
public $name;
// #var int edit ;
public $edit;
public function setName($var=null) {
.. do stuff..
}
}
Every time when I add an property, class requires additional get, set function. Can I handle this differently except using __set()?
Here is my code:
class {
$property = "something";
public static function myfunc() {
return $this->property;
}
}
but PHP throws this:
Using $this when not in object context
I know, the problem is using $this-> in a static method, ok I remove it like this:
class {
$property = "something";
public static function myfunc() {
return self::property;
}
}
But sadly PHP throws this:
Undefined class constant 'property'
How can I access a property which is out of a static method in it?
Generally, you should not do it. Static methods don't have an access to instance fields for a reason. You can do something like this, though:
// define a static variable
private static $instance;
// somewhere in the constructor probably
self::$instance = $this;
// somewhere in your static method
self::$instance->methodToCall();
Note that it will work only for a single instance of your class, since static variables are shared between all instances (if any).
You'll also need to add a bunch of validations (e.g. is $instance null?) and pay attention to all the implementation details that may cause you some troubles.
Anyway, I don't recommend this approach. Use it at your own risk.
Explaination
If you want to use a variable that wont change inside a class you don't want to instanciate, you need to use the static keyword in order to access it later in a method.
Also, you need a name for your class.
And finally, if you didn't specify a keyword as protected or public, you variable may be accessible outside the word, and so the method would be pointless. So I assume you need a protected value in order to use the method to call that variable.
Source-code
class Foo {
protected static $property = 'something';
public function getProperty() {
return self::$property;
}
}
echo Foo::getProperty(); /* will display : something */
echo Foo::$property; /* change from protected to public to use that syntax */
Documentation
PHP : classes.
PHP : static.
PHP : visibility.
I am using PHPUnit to make a mock class for testing.
class Item extends Object {
protected static $_cache;
}
I am pretty certain mocking does something like this ( please correct me if I'm wrong ):
class Mock_Item_randomstring extends Item {
}
When my Item's cache gets populated, it's checking to see that the object being passed in is an instance of Item. Since the mock is not explicitly defining $_cache, it fails the check for instance type.
PHP doesn't really document the reflection functions well at all. Is there a way to set the static variable after the fact so the class would become
class Mock_Item_randomstring extends Item {
protected static $_cache;
}
EDIT
I played around with reflection methods and ran into a variety of issues. Here is one that I'm confused about:
$mock = $this->getMock( 'Item', array( '_func' ), array(
$argument1, $argument2
));
$mock = new ReflectionClass($mock);
$mock->staticExpects( $this->exactly(2) )->method( '_func' );
I was under the assumption reflections copy the entire class. I get this error:
Call to undefined method ReflectionClass::staticExpects()
I tend to use a somewhat nasty trick for per-class static variables in subclasses:
class A {
protected static $cache;
public static function getCache() {
return static::$cache;
}
public static function setCache($val) {
static::$cache = & $val; // note the '&'
}
}
class B extends A {}
A::setCache('A');
B::setCache('B');
A::getCache(); // 'A'
B::getCache(); // 'B'
Of course, it is the best to avoid static variables in the first place. Use a dedicated cache object and inject it when the class is instantiated.
You don't have to. \Closure::bind lets you read and assign private and protected static properties. See the example code on http://www.php.net/manual/en/closure.bind.php
I'm using NetBeans as my IDE. Whenever I have some code that uses another function (usually a factory) to return an object, typically I can do the following to help with hinting:
/* #var $object FooClass */
$object = $someFunction->get('BarContext.FooClass');
$object-> // now will produce property and function hints for FooClass.
However, when I use an object's property to store that class, I'm at a bit of a loss how to do the same, as trying to use #var $this->foo or #var foo will not carry the hinting through:
use Path\To\FooClass;
class Bar
{
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo //does not have hinting in IDE
}
}
I have tried in the docblock for the class, or using the inline comments above protected $foo or where foo is set to the instance.
The only workaround I have found so far is to:
public function bat()
{
$this->foo = FactoryClass::get('Foo');
/* #var $extraVariable FooClass */
$extraVariable = $this->foo;
$extraVariable-> // now has hinting.
}
I would really like to have the hinting be class-wide though, as many other functions could potentially use $this->foo, and knowing the class's methods and properties would be useful.
Surely there is a more straightforward way...
I cannot say how it works in Netbeans, but in PHPEclipse, you would add the hint to the declaration of the variable itself:
use Path\To\FooClass;
class Bar
{
/**
* #var FooClass
*/
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo // should now have hinting
}
}
Given
class Bar
{
protected $foo;
public function bat()
{
$this->foo = FactoryClass::get('Foo'); // Returns an instance of FooClass
$this->foo //does not have hinting in IDE
}
}
The IDE is trying to get the declaraion from FactoryClass::get which probably has no docblock return type. The problem is if this factory method can return any number of classes, there isn't much you can do except for using your workaround.
Otherwise, it wont know the difference between FactoryClass::get('Foo') or FactoryClass::get('Bar') since those two calls most likely would return objects of different types.