There are two classes:
class Product {
public id;
public name;
}
class OrderCustomer
{
public $DeliveryDate;
public $PhotoBefore = 0;
public Product[] products;
}
So, I attempt to specify property products in class OrderCustomer as Product type. It should be array of products.
How to do that in php?
As mentioned in the comments, PHP is not a strongly typed language and what you are looking for is not possible, at least not in the same way it is in languages like C# and Java. You can get close to that functionality by enforcing what is added to the $products class member by making adding values to it limited to a setter class method that only accepts variables whose type is Product.
To get the type hint you would need to use a Doc Prop explicitly stating that that variable is intended to hold and array products.
class Product {
public $id;
public $name;
}
class OrderCustomer
{
public $DeliveryDate;
public $PhotoBefore = 0;
/** #var Product[] */
public $products = [];
public function addToProductArray(Product $product)
{
$this->products[] = $product;
}
}
Because $products is now protected make sure you also make a getter for getting its value from outside of the class (i.e. by the calling code) if you intend for it to be accessed that way.
Related
i have this class
class Product {
public $name;
private $price2;
protected $number;
function getNmae() {
return $this->name;
}
function getPrice() {
return $this->price2;
}
function getNumber() {
return $this->number;
}
}
and here I can use private price without any problem?
<?php
include 'oop_test.php';
class Banana extends Product {
}
$ba = new Banana();
echo $ba->price2 = 2000;
?>
the result like this:
I cannot understand how could I assign value to private variable ?
Looks like you've created a property on-the-fly in this case. A reduced sample shows this:
<?php
class Product {
private $price2;
function getPrice() {
return $this->price2;
}
}
class Banana extends Product {}
$ba = new Banana();
$ba->price2 = 2000;
echo 'On the fly property: ' . $ba->price2;
echo 'Private property: ' . $ba->getPrice();
That code prints 2000 for the property price2, but nothing is returned from getPrice - so you haven't really written the property price2 on the Product class, but created a new one within the Banana class.
The "original" property from the Product class is not involved here, as it is a private property and thus not available in the Banana class after all.
Actually the display of 2000 does not mean that the parent class value is set.
Your statement only creates a new variable for the class itself, not for the parent class because the price2 is not declared as protected / public.
It explains why the system does not throw an error in your case
Recap:
public scope to make that property/method available from anywhere, other classes and instances of the object.
private scope when you want your property/method to be visible in its own class only.
protected scope when you want to make your property/method visible in all classes that extend current class including the parent class
Try create a method (say setPrice) to set the price2 in the parent class if you need to set a value for this private variable
<?php
class Product {
private $price2;
function setPrice($a) {
$this->price2 = $a;
}
function getPrice() {
return $this->price2;
}
}
class Banana extends Product {
}
$ba = new Banana();
//$ba->price2 = 2000;
/*The above will not throw an error even executed
because it is creating a property within
the banana class*/
$ba->setPrice(2001);
echo $ba->getPrice();
/*The above is for setting / getting
the price2 private variable
in the parent class-just for illustration*/
?>
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
i am trying to understand the concepts of oop in php.
my question: why properties (here: $result and $operation) are added in the class, but it works fine if i dont add them and var_dump this, so the objects property is set.
example:
class Calculator {
protected $result;
protected $operation;
public function setOperation ( $operation) {
$this->operation = $operation;
}
}
and here without:
class Calculator {
public function setOperation ( $operation) {
$this->operation = $operation;
}
}
why is this important, sometimes i see a class without properties declared on top?
This is so you can define visibility. Sometimes you have a class that holds a variable which you want to modify using methods of the same class only. If you do not explicity define variables inside a class, they are always public and can be modified in any scope.
http://php.net/manual/en/language.oop5.visibility.php
I need to write an order status class (file Status.php), which will contain certain statuses of an order in my internet store, which I am currently working on.
Desired statuses are:
-Pending (User still haven't confirmed items in the basket).
-Confirmed (User confirmed items in the basket as his order).
-Paid (User has paid for items he bought)
-Complete (Items were sent to user via post office)
It's probably very simple task, but I don't know how to start my work and build this class.
This is my Status.php code so far:
<?php
class Status {
private $id;
private $name;
function __construct() {
$this->id = -1;
$this->name = "";
}
function getId() {
return $this->id;
}
function getName() {
return $this->name;
}
function setName($name) {
$this->name = $name;
}
}
?>
Big thanks to everyone who helps under this post :).
but I don't know how to start my work and build this class.
The code you posted is not a class but some procedural code disguised as OOP. Let's assume I create an object of type Status:
$status = new Status();
Is this an object? What is the value of this new object? It looks like an object but it is just some empty memory. No data, no behaviour, just useless code.
The constructor of a properly implemented class constructs the object, prepares it for usage. When the constructor returns, the object must be fully set; no further $status->setId() and $status->setName() should be required.
This is how I suggest you to define your class:
class Status {
private $id;
private $name;
public function __construct($id, $name) {
// Validate arguments; an object must always be valid
if (! is_int($id) || $id <= 0) {
throw new InvalidArgumentException('$id must be a positive non-zero number.');
}
if (! is_string($name) || empty($name)) {
throw new InvalidArgumentException('$name must be a non-empty string.');
}
// The arguments are valid; go ahead and construct the object
$this->id = $id;
$this->name = $name;
}
}
Now let's create some Status objects:
$pending = new Status(1, 'pending');
$confirmed = new Confirmed(2, 'confirmed');
Depending on how you plan to use the properties of the objects, you will probably not pass the ID as argument to the constructor but generate it internally or use a fixed list of values.
Or you can go one step further, declare the properties and the constructor of class Status as protected and create one child class for each possible value of $status:
class Status {
protected $id;
protected $name;
protected function __construct($id, $name) {
// ...
}
// ...
}
class Pending extends Status {
public function __construct() {
parent::__construct(1, 'pending');
}
}
class Confirmed extends Status {
public function __construct() {
parent::__construct(2, 'confirmed');
}
}
class Paid extends Status {
public function __construct() {
parent::__construct(3, 'paid');
}
}
The properties are protected to allow the methods of the children classes access them (when they are private only the methods of class Status see them). The constructor is protected to prevent the creation of objects of type Status but allow the methods of the children classes (their constructors actually) invoke it. Always call the constructor of the parent class in the constructor of the child class.
This way, when you need to create an object that represents a status, all you have to do is:
$pending = new Pending();
$confirmed = new Confirmed();
// a.s.o.
Don't rush to add the "getters" (the methods that access the object properties and provides the to the client code) you defined in your code. Think what do you want to do with the values stored in a Status object and check if that code can stay in the Status class too. Put code specific to the "pending" status (f.e.) into the Pending class, not into the Status class.
If Status::$name is used only for display then you better implement __toString() instead of getName():
class Status {
// ...
public function __toString()
{
// It is vital here to enforce the type of $this->name into the constructor
// __toString() must return a string
return $this->name;
}
}
This way your code will look nice:
$pending = new Pending();
echo('The status is: '.$pending);
// Output:
// The status is: pending
I have an ActiveRecord class where I need to have some kind of dynamic variables in it.
The dynamic variable should be created for each existing variable. For example if I have a variable $test in my class, then it should also has $testDyn.
Example:
class User extends ActiveRecord
{
public $name;
public $age;
public $address;
/**
* dynamic vars
*/
public $nameDyn;
public $ageDyn;
public $addressDyn;
public function tableName()
{
return 'user';
}
...
}
There are ActiveRecords which have many variables and it would not be the best idea to do and manually create those dynamic vars for each variables. Are there any easy way / behavior which can do this?
You can create custom behavior for these purposes and attach for every needed model.
As for properties, you can explicitly specify it via parameter through behavior config, or use something like this:
$reflect = new ReflectionClass(new YourClass());
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
More info and examples are available here.
You can additionally use some naming convention.
For dynamic properties you can override __get() magic method in behavior. See here.
Yii2 example (taken from yii2tech/ar-linkmany behavior by Paul Klimov, one of the framework contributors):
/**
* PHP getter magic method.
* This method is overridden so that relation attribute can be accessed like property.
*
* #param string $name property name
* #throws UnknownPropertyException if the property is not defined
* #return mixed property value
*/
public function __get($name)
{
try {
return parent::__get($name);
} catch (UnknownPropertyException $exception) {
if ($name === $this->relationReferenceAttribute) {
return $this->getRelationReferenceAttributeValue();
}
throw $exception;
}
}
To understand more about behaviors concept, please refer to according official docs section Behaviors.