I am writing this simple code and do not know what the issue with constructor is:
class Animal {
public $_type;
public $_breed;
public function __construct ($t, $b) {
echo "i have initialized<br/>";
$this ->_type = $t;
$this ->_breed = $b;
echo "type is " .$_type. "<br/>";
echo "breed is " .$_breed. "<br/>";
}
public function __destruct () {
echo "i am dying";
}
}
$dog = new Animal("Dog", "Pug");
Why do you have a space after $this? Remove the space.
Also, add $this when calling a variable.
class Animal {
public $_type;
public $_breed;
public function __construct ($t, $b) {
echo "i have initialized<br/>";
$this->_type = $t; // <-- remove space
$this->_breed = $b; // <-- remove space
echo "type is " .$this->_type. "<br/>"; // <-- add $this
echo "breed is " .$this->_breed. "<br/>"; // <-- add $this
}
public function __destruct () {
echo "i am dying";
}
}
You are initializing them fine, but retrieving them wrong...
Since $_type and $_breed are scoped at the object level, you need to tell PHP what scope you're referencing them in.
Therefore, instead of
echo $_breed;
You need
echo $this->_breed;
On a side note, it's very strange practice to prefix variable names with _ these days, even moreso if they are public variables. This will likely confuse other developers working with your code.
Try this (note the echo lines):
class Animal {
public $_type;
public $_breed;
public function __construct ($t, $b) {
echo "i have initialized<br/>";
$this->_type = $t;
$this->_breed = $b;
//You have to use '$this' keyword to access
//class attibutes:
echo "type is " . $this->_type . "<br/>";
echo "breed is " . $this->_breed . "<br/>";
}
public function __destruct () {
echo "i am dying";
}
}
$_type and $_breed is variable of class so you need to use using this keyword
echo "type is " .$this->_type. "<br/>";
echo "breed is " .$this->_breed. "<br/>";
Although this not violate the syntax of the php, I suggest that this two lines
echo "type is " .$dog->_type. "<br/>";
echo "breed is " .$dog->_breed. "<br/>";
must not be put in __construct() instead use this outside the class,
Like this,
class Animal {
public $_type;
public $_breed;
public function __construct ($t, $b) {
$this ->_type = $t;
$this ->_breed = $b;
}
public function __destruct () {
echo "i am dying";
}
}
$dog = new Animal("Dog", "Pug");
echo "i have initialized<br/>";
echo "type is " .$dog->_type. "<br/>";
echo "breed is " .$dog->_breed. "<br/>";
With constructor property promotion in PHP 8, you can now declare and set properties as parameters of the constructor.
See here.
class someClass
{
// Can delete these declarations and put them in the constructor instead //
/*
protected int $id;
protected string $name;
protected int $type;
protected string $frontend_name;
protected int $account_id;
protected someOtherClass $object;
*/
public function __construct(
protected int $id,
protected string $name,
protected int $type,
protected string $frontend_name,
protected int $account_id,
protected someOtherClass $object,) // you can also have a trailing comma now!
{
$this->init();
}
protected function init() {
// do other stuff
}
}
Related
the code is self-explanatory, but the problem is, that I can't override the static variable through method calls, so the static variable of the class gives a value, but the value that I get through the objects is different.
Class dbEntity {
protected static $connection;
private static $name = "dbEntity";
public static function getName() {
return self::$name;
}
public static function setConnection($connection) {
self::$connection = $connection;
}
public static function getConnection() {
return self::$connection;
}
}
Class Patient extends dbEntity {
public static $connection = "patientConnection";
public static $name = "Patient";
}
$p = new Patient();
$p->setConnection("myConnection");
echo $p->getConnection() . "\n"; //myConnection
echo Patient::$connection . "\n"; //patientConnection
echo $p->getConnection() . "\n"; //myConnection
Ulrich is correct, if you change the following lines from self:: to static:: the code works as you're expecting it to.
public static function getName() {
return static::$name;
}
public static function setConnection($connection) {
static::$connection = $connection;
}
public static function getConnection() {
return static::$connection;
}
...
echo $p->getConnection() . "<br>"; //myConnection
echo Patient::$connection . "<br>"; //myConnection
echo $p->getConnection() . "<br>"; //myConnection
You need late static binding, see the official documentation:
https://www.php.net/manual/en/language.oop5.late-static-bindings.php
So I'm following a tutorial about OOP in PHP and got stuck in understanding how __get() function works. Here's the code:
<?php
class Animal{
protected $name;
protected $favorite_food;
protected $sound;
protected $id;
public static $number_of_animals = 0;
const PI = "3.14159";
//function to return the name
//encapsulation
function getName(){
//when you want to refer attribute in a class
return $this->name;
}
//initialize things
function __construct(){
//generate random 100-10
$this->id = rand(1,10);
echo $this->id ." has been assigned<br/>";
//akses static attribute in a class
Animal::$number_of_animals++;
}
//destruct the object
function __destruct(){
echo $this->name ." is being destroyed :(";
}
//getter : to get protected attribute of a function
function __get($name){
echo "Asked for " . $name . "<br/>";
return $this->$name;
}
//setter : set the attribute to
function __set($name, $value){
switch($name){
case "name" :
$this->name = $value;
break;
case "favorite_food" :
$this->favorite_food = $value;
break;
case "sound" :
$this->sound = $value;
break;
default :
echo $name ."Name not found";
}
echo "Set " .$name. " to " .$value. "<br/>";
}
function run(){
echo $this->name. " runs<br/>";
}
}
class Dog extends Animal{
function run(){
echo $this->name. " runs like crazy<br/>";
}
}
$animal_one = new Animal();
$animal_one->name = " SPOT";
$animal_one->favorite_food = " MEAT";
$animal_one->sound = " RUFF";
echo $animal_one->name ." says". $animal_one->sound. " give me some " .$animal_one->favorite_food. " my id is " .$animal_one->id. " total animal is " .Animal::$number_of_animals. "<br/><br/>";
?>
The output will be like this :
5 has been assigned
Set name to SPOT
Set favorite_food to MEAT
Set sound to RUFF
Asked for name
Asked for sound
Asked for favorite_food
Asked for id
SPOT says RUFF give me some MEAT my id is 5 total animal is 1
SPOT is being destroyed :(
When I try to change the argument and value in __get() function to another attribute like $sound or $favorite_food, it doesn't give any change to the output. The output will still the same. I don't get it why we should set it only to $name.
The name of the parameter inside any function is scoped to that function alone, and doesn't have any reference anywhere else.
You're probably getting confused in that your local function parameter $name has the same name as one of it's class properties $this->name
Notice in your __get method, $name is a stand-in variable for what could be any protected/private property, which is dynamically evaluated at run-time:
$this->$name
as opposed to a hard-coded property
$this->name
Consider this example:
class MyClass {
protected $one = 'first';
protected $name = 'fred';
public function __get(String $property){
return $this->$property;
}
public function getOne(){
return $this->one;
}
public function foo(String $variable_could_be_named_anything){
return $variable_could_be_named_anything;
}
}
$object = new MyClass;
echo $object->one; // first (using __get)
echo $object->getOne(); // first
$object->two = 'second'; // because this property isn't declared protected, accessed normally
echo $object->two; // second
$name = 'jon';
echo $object->name; // fred
echo $object->foo($name); // jon
echo $object->three; // PHP Notice: Undefined property: MyClass::$three
$object->one = 'something'; // Fatal error: Cannot access protected property
I need different __toString() for the same class.
Example
I have the Person class that contains a firstname and a surname. According to current context I wish to display it with different order, formatting etc. Imagine three scenarios:
the firstname comes first, followed by space and surname Thomas Müller
the surname comes first and uppercased, followed by space and the firstname MÜLLER Thomas
the surname comes first, followed by comma, then space and the firstname Müller, Thomas
I can create public methods for each display.
<meta charset='utf-8'/>
<?php
class Person
{
protected $firstname;
protected $surname;
public function __construct($firstname, $surname)
{
$this->firstname = $firstname;
$this->surname = $surname;
}
public function toStringWithFirstnameFirst()
{
return $this->firstname . " " . $this->surname;
}
public function toStringWithSurnameFirstUppercase()
{
$surnameConverted = mb_convert_case($this->surname, MB_CASE_UPPER, "UTF-8");
return $surnameConverted . " " . $this->firstname;
}
public function toStringWithSurnameFirstAndFirstnameAfterComma()
{
return $this->surname . ", " . $this->firstname;
}
}
$person = new Person("Thomas", "Müller");
echo $person->toStringWithFirstnameFirst() . "<br/>";
echo $person->toStringWithSurnameFirstUppercase() . "<br/>";
echo $person->toStringWithSurnameFirstAndFirstnameAfterComma() . "<br/>";
?>
But I stuck with DescriptiveButVeryLongToStringMethodNames.
I wish to have simply echo $person; in the code.
Solution: store the class state in the static members
My first solution is to place switch-case inside __toString() method. Conditional statement depends on the class state stored in the static variable self::$chosenToStringMethod. So I need static method to set the class state and also class constants that serve as enums.
<meta charset='utf-8'/>
<?php
class Person
{
protected $firstname;
protected $surname;
const PRINT_FIRSTNAME_FIRST = 1;
const PRINT_SURNAME_FIRST_UPPERCASE = 2;
const PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA = 3;
static private $chosenToStringMethod;
public function __construct($firstname, $surname)
{
$this->firstname = $firstname;
$this->surname = $surname;
}
static public function setToStringMethod($choice)
{
self::$chosenToStringMethod = $choice;
}
private function toStringWithFirstnameFirst()
{
return $this->firstname . " " . $this->surname;
}
private function toStringWithSurnameFirstUppercase()
{
$surnameConverted = mb_convert_case($this->surname, MB_CASE_UPPER, "UTF-8");
return $surnameConverted . " " . $this->firstname;
}
private function toStringWithSurnameFirstAndFirstnameAfterComma()
{
return $this->surname . ", " . $this->firstname;
}
public function __toString()
{
switch (self::$chosenToStringMethod) {
case self::PRINT_FIRSTNAME_FIRST:
return $this->toStringWithFirstnameFirst();
break;
case self::PRINT_SURNAME_FIRST_UPPERCASE:
return $this->toStringWithSurnameFirstUppercase();
break;
case self::PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA:
return $this->toStringWithSurnameFirstAndFirstnameAfterComma();
break;
default:
return "No __toString method set";
break;
}
}
}
$person = new Person("Thomas", "Müller");
echo $person . "<br/>";
Person::setToStringMethod(Person::PRINT_FIRSTNAME_FIRST);
echo $person . "<br/>";
Person::setToStringMethod(Person::PRINT_SURNAME_FIRST_UPPERCASE);
echo $person . "<br/>";
Person::setToStringMethod(Person::PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA);
echo $person . "<br/>";
?>
I see some disadvantages of this solution:
Person class is getting heavy
switch-case statements can hide some mistakes
I wish Person class containing only its own functionality not all kinds of toStrings. I would rather have some pattern that can dynamically inject __toString().
ok, I assume the reason, why you're asking, is that you'd like to keep things clean but there's no way to set __toString() method to an existing object, so the best solution would be to split functionality
first create a PersonRender:
class PersonRender
{
const PRINT_FIRSTNAME_FIRST = 1;
const PRINT_SURNAME_FIRST_UPPERCASE = 2;
const PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA = 3;
static public $chosenToStringMethod;
private $person;
public function __construct($person)
{
$this->person = $person;
}
public function render()
{
switch (self::$chosenToStringMethod)
{
case self::PRINT_SURNAME_FIRST_UPPERCASE :
return $this->toStringWithSurnameFirstUppercase();
case self::PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA :
return $this->toStringWithSurnameFirstAndFirstnameAfterComma();
default :
return $this->toStringWithFirstnameFirst();
}
}
private function toStringWithFirstnameFirst()
{
return "{$this->person->firstname} {$this->person->surname}";
}
private function toStringWithSurnameFirstUppercase()
{
$surnameConverted = mb_convert_case($this->person->surname, MB_CASE_UPPER, "UTF-8");
return "{$surnameConverted} {$this->person->firstname}";
}
private function toStringWithSurnameFirstAndFirstnameAfterComma()
{
return "{$this->person->surname}, {$this->person->firstname}";
}
}
and then the Person class:
class Person
{
public $firstname, $surname;
public function __construct($firstname, $surname)
{
$this->firstname = $firstname;
$this->surname = $surname;
}
public function __toString() {
$render = new PersonRender($this);
return $render->render();
}
}
and a little test:
$person = new Person('Foo', 'Bar');
echo $person;
echo '<hr />';
PersonRender::$chosenToStringMethod = PersonRender::PRINT_SURNAME_FIRST_UPPERCASE;
echo $person;
EDIT 1
to keep the code clean, the Person entity class should of course have private props firstname and surename and methods set and get
In my opinion the most clean way would be to just provide getter for firstname and surname and manually assemble the strings where you need them. In your current solutions you are just polluting your workspace with unnecessary classes and make the class Person more complex than it should be.
I tried this and I get an error when I try to instantiate class "first" inside of class "second".
The commented sections inside of class "second" cause errors.
class first {
public $a;
function __construct() {
$this->a = 'a';
}
}
class second {
//$fst = new first();
//public showfirst() {
//$firsta = $this->first->a;
// echo "Here is first \$a: " . $firsta;
//}
}
EDIT:
This results in a server error even though all I have in class "second" is the instantiation of class "first".
class second {
$fst = new first();
//public showfirsta() {
// $firsta = $this->fst->a;
// echo "Here is first \$a: " . $firsta;
//}
}
try this:
class First {
public $a;
public function __construct() {
$this->a = 'a';
}
public function getA() {
return $this->a;
}
}
class Second {
protected $fst;
public function __construct() {
$this->fst = new First();
}
public function showfirst() {
$firsta = $this->fst->getA();
echo "Here is first {$firsta}";
}
}
$test = new Second();
$test->showfirst();
$fst = new first();
You cannot declare a variable that instantiates a new class outside of a function. The default value cannot be variable. It has to be a string, a number, or possibly an array. Objects are forbidden.
public showfirst() {
You forgot the word function in there.
$firsta = $this->first->a;
You have no class variable $first declared. You named it $fst and would reference it as $this->fst.
echo "Here is first \$a: " . $firsta;
}
For your purposes (whatever those may be):
class second {
public function showfirst() {
$fst = new first();
$firsta = $fst->a;
echo "Here is first \$a: " . $firsta;
}
}
You can instantiate a class inside another. In your case, in your both example you keep referring to the wrong variable. Also, you can't assign a class in the declaration of a property:
class second {
public $fst;
public function showfirsta() {
$this->fst = new first();
$firsta = $this->fst->a;
echo "Here is first \$a: " . $firsta;
}
}
I am struck at overloading the parent's class methods from an inherited child at level2.
abstract class parent
-> child1 extends parent
-> final class child2 extends child1
I want to overload the methods of parent in child2
abstract class Shape
{
protected $length;
protected $height;
protected $a;
protected $b;
protected $c;
public function getCoordinates($length,$height)
{
$this->length=$length;
$this->height=$height;
}
public function getSides($a,$b,$c)
{
$this->a=$a;
$this->b=$b;
$this->c=$c;
}
abstract public function area();
abstract public function perimeter();
abstract public function display();
}
class rectangle extends Shape
{
public function area()
{
return round(($this->length)*($this->height),2);
}
public function perimeter()
{
return round(2*(($this->a)+($this->b)),2);
}
public function display()
{
echo "area is :". rectangle::area() . "<br>";
echo "perimeter is : ". rectangle::perimeter() ."<br>";
}
}
final class triangle extends rectangle
{
function __call($method_name, $arguments) // this is wrong ........please modify here to call area(),which is in shape class();
{
$accepted_methods = array("getCoordinates","area","perimeter");
}
public function area()
{
return round((($this->length)*($this->height)*($this->width)/2),2);
}
public function perimeter()
{
return round((($this->a)+($this->b)+($this->c)),2);
}
public function getCoordinates($length,$height,$width)
{
$this->length=$length;
$this->height=$height;
$this->width=$width;
}
public function display()
{
echo "area is :". triangle::area() . "<br>";
echo "perimeter is : ". triangle::perimeter() ."<br>";
}
}
$r=new rectangle();
$r->getCoordinates(1,2,4);
$r->getSides(6,2);
$r->display();
$ot = new triangle();
$ot->getCoordinates(1,2,4);
$ot->getSides(6,2,3);
$ot->display();
?>
Thanks in advance
$r->getSides(6,2);
Your abstract class demands three arguments! Plus the function is actually a setter method. You should name it setSides();. Same with getCoordinates().
Update: I think you are confusing inheritance with overloading. Here is an example for overloading with __call. I assume that's not what you are trying to do but what you have in your example. Maybe this will help.
abstract class overloadTestAbstract {
public function printOne($show) {
echo __METHOD__ . ': ' . $show . '<br />';
}
}
class overloadTestOne extends overloadTestAbstract {
public function __call($method,$arguments) {
$methods = array('printOne','printTwo','printThree');
if ( in_array($method,$methods) ) {
echo __METHOD__ . ' :OVERLOAD METHOD: ' . $arguments[0] . '<br />';
} else {
echo 'We are so sorry, but this method is available';
}
}
public function printTwo($show) {
echo __METHOD__ . ': ' . $show . '<br />';
}
}
Then if you do this:
$test = new overloadTestOne();
$test->printOne('Hello World');
$test->printTwo('Goodbye World');
$test->printThree('Hello World, again');
$test->printFour('Goodbye World, again');
you will get this
// print results
'overloadTestAbstract::printOne: Hello World'
'overloadTestOne::printTwo: Goodbye World'
'overloadTestOne::__call :OVERLOAD METHOD: Hello World, again'
'We are so sorry, but this method is available'
Although I have printOne and printTwo in the overload __call as accepted methods they are not used because these methods are already defined, they are handled by the existing methods as expected. On the other hand printThree gets overloaded because the method does not exist. Same with printFour but that method has no intend to print the argument. The array you have defined with the accepted methods doesn't do a thing. It is just an array. You have to assign some task to these methods or return some error like I did.