How to make an exception to "private" constructors? - php

I want to declare a non-public constructor so it's users of the class can't call new Message() directly but have to instantiate the object from a static builder method declared on an abstract class that Message extends.
So far my code is:
abstract class SqlDecodable {
public function instanceFromRawSql (array $rawSql) {
$newInstanceToReturn = new static() // problem is here
// .. map the new instance ..
return $newInstance ;
}
}
// for exemple...
class Message extends SqlDecodable {
private $receiverId ;
private $senderId ;
private $text ;
private/protected/public?? function __construct() {
// problem here : this constructor should be usable only by
parent class, not for user of Message
}
static function propertiesToSqlFields() {
return [
"receiverId" => "receiver_id_field_in_db",
"senderId" => "sender_id",
"text" => "text"
]
}
}
This is actually more complicated, but I simplified the system for this question
When I implement my method instanceFromRawSqlArray, I have to create a new instance of the child class: $instanceToReturn = new static(), and set the variables one by one afterwards.
Though, I don't want to let a __construct that takes no args in my model classes. I don't want the dev user of Message to be able to new Message().
This constructor should be usable only by instanceFromRawSqlArray.
The problem is that, as I saw, there is no C++ friends class in PHP. I can't make my __construct protected, because, as I saw, protected methods are accessibles for childs, not for parent.
Do you have ideas to map this new instance in the method instanceFromRawSqlArray, without creating any constructor or setter that would corrupt my model class "encapsulation protections"?

You were very close. You can simply declare your constructor to be protected.
Instantiating the class directly won't work, but you can call new from the static method declared in the abstract class.
E.g.:
abstract class SuperAbstract {
static function create() {
return new static();
}
}
class Extended extends SuperAbstract {
private $hello = '';
protected function __construct() {
$this->hello = "world";
}
public function hello() {
return "hello " . $this->hello;
}
}
// works
$a = Extended::create();
echo $a->hello(); // outputs "hello world"
// can't touch this. This will fail because the constructor is `protected`.
$b = new Extended();
Of course, since it's protected the constructor could also be called from children classes. That's unavoidable, as long as children classes are a possibility. But you could also declare Extended as final, making extension of the class impossible. Thus, it would only be possible to create new instances from the factory method defined in the abstract parent.
final Extended extends SuperAbstract
protected function __construct() { }
}
You can see it working (and failing), here: https://3v4l.org/LliKj

Related

PHP type hinting from object to my class, not possible?

I've setup an abstract class that must execute methods from another instanciated class.
So I setup an anonymous class that extends my abstract class and in the implemented method, I want to access the methods of that "other instanciated class", but for my IDE to help me to find the correct name of the methods and arguments, I must write somewhere the type.
abstract class blah
{
protected object $reference;
public function __construct(object $reference)
{
$this->reference = $reference;
$this->hello();
}
abstract public function hello();
}
class world
{
public function __construct()
{
new class($this) extends blah {
public function hello()
{
$this->reference->display();
}
};
}
public function display()
{
echo 'hello world';
}
}
new world();
I can't change the type from "object" to something else to allow my IDE to show the methods
public function __construct()
{
new class($this) extends blah {
protected world $reference;
public function hello()
{
$this->reference->display();
}
};
}
this throws
Fatal error: Type of blah#anonymous::$reference must be object (as in class blah)
I could copy the attribute "reference" to another variable and set a typehint to "world", but that's not the best
public function __construct()
{
new class($this) extends blah {
protected world $reference;
public function hello()
{
/** #var world $hello */
$hello = $this->reference;
$hello->display();
}
};
}
what better solution do I have ?
edit: this seems to work, is it the best way ?
new class($this) extends blah {
/** #var world $reference */
protected object $reference;
public function hello()
{
$this->reference->display();
}
};
It's worth understanding why PHP is telling you that you can't change the property type.
Since this is a protected property, it can be read and written by either the parent or child class. For instance, the parent class might have methods like this, which would be inherited by the child class:
public function getReference(): object {
return $this->reference;
}
public function setReference(object $newReference): void {
$this->reference = $newReference;
}
public function setReferenceToDefault(): void {
$this->reference = new SomethingBoring;
}
If you specify world as the type of $reference in a child class, the getReference method will work fine - any instance of world is an object, so the return type is OK. But the setReference() and setReferenceToDefault() methods would fail, because they try to assign something that isn't of type world.
Essentially, by declaring a protected property on the base class, you are setting a contract for all child classes, and PHP is enforcing it for you. The technical term for this is "contravariance": a child class can be more generous in what it accepts, but it can't reject values the parent would accept. The opposite is "covariance", which applies to returning values: the child class can be more specific about what it will return, but can't return something the parent class never would. When something is both input and output, you get both restrictions, so can't vary the type at all.
Since you don't actually use the property on the base class, this restriction isn't actually needed, so in this case one option is simply not to define the property or constructor on the base class. In other cases, you might want to use traits, which are "compiler assisted copy-and-paste": they don't assert any relationship between two classes, but they save you writing the same code more than once.
If you want it only for IDE, then use documentation block to overwrite type declaration (if IDE supports it):
/**
* #property world $reference
*/
new class($this) extends blah {
public function hello()
{
$this->reference->display();
}
};
E.g. PHPStorm, while not supporting that for anonymous class, supports for normal class description:

Too few arguments error even though argument passed properly [duplicate]

This question already has an answer here:
Too few arguments to function Db::__construct(), 0 passed in
(1 answer)
Closed 4 years ago.
When I run this, it complains that
Too few arguments to function Base::__construct(), 0 passed in [FileName].php on line 18 and exactly 1 expected
class Base {
protected $var;
public function __construct($var){
$this->var = $var;
}
}
class ChildA extends Base {
public function handle(){
echo $this->var;
}
}
$base = new Base(10);
(new ChildA())->handle();
But as you can see, I have passed:
$base = new Base(10);
Edit: As mentioned in answers and comments, the problem was not in here:
$base = new Base(10);
...but here:
(new ChildA())->handle();
What am I doing wrong? Also, what I am trying to achieve is that, there are many ChildX classes who need to extend Base and all these child classes must have the $var property. So, Instead of duplicating the $var in each child classes, I wanted to reduce typing effort by putting it in a parent class. But apparently, my plan didn't work out.
Your problem is actually this line:
(new ChildA())->handle();
Do this instead:
$child = new ChildA(10);
$child->handle();
You don't need to instantiate the base class separately.
One other thing. When both parent and child classes have a constructor, you can put parent::__construct($whatever); in the child's constructor to run the parent code also.
This is a simple concept of class instantiation, I think which you are getting it wrong.
When you did
$base = new Base(10);
you are creating an instance of the Base class only.
However, the following statement invokes another instance of the Child class. Note, the base variable and this child instance are two different entities, hence their behaviour will not impact each other.
(new ChildA())->handle();
So, currently base variable will have a value of 10, but the handle() function call cannot be instantiated since it requires an argument which is to be supplied to the new instance of the Base class.
You'll need to instantiate the child class, due to which the constructor of the inherited class will be called first and your variable will be set. Your handle() function can then successfully return the correct value.
$child = new Child(10); $child->handle(); //returns 10
When you extend a class, all the methods on that class exist on the child class by default, including "magic" methods like __construct.
So when you write this:
class Base {
protected $var;
public function __construct($var){
$this->var = $var;
}
}
class ChildA extends Base {
public function handle(){
echo $this->var;
}
}
Your child class actually looks something like this:
class ChildA extends Base {
// Inherited property from Base class
protected $var;
// Inherited method from Base class
public function __construct($var){
$this->var = $var;
}
// New method in this class
public function handle(){
echo $this->var;
}
}
This is a slight simplification, as we'll see below, but it explains what happens in your example:
$base = new Base(10);
$base is an instance of the Base class; it is completely separate from any other instance of that class, and from any instance of the ChildA class.
(new ChildA())->handle();
This attempts to run the __construct method in the ChildA class; the copy of that method inherited from Base requires an argument, but you didn't supply one, so you get an error. It is expecting you to call it like this:
(new ChildA(10))->handle();
If we define __construct directly in the child class, we can give it a different number of arguments:
class ChildB extends Base {
public function __construct() {
// Do nothing
}
public function handle(){
echo $this->var;
}
}
(new ChildB())->handle();
But now we have a different problem: the logic in the Base class isn't running, so $this->var is never set to anything.
This is where I over-simplified things above. In reality, the parent __construct method and child __construct method both exist, they just "hide" each other. You can access the parent copy with the syntax parent::method.
So you can make a child class, with a constructor which takes no arguments, and when that constructor is run, run the base constructor with a hard-coded value of 10:
class ChildC extends Base {
public function __construct() {
// Run the constructor in the Base class
parent::__construct(10);
}
public function handle(){
echo $this->var;
}
}
(new ChildC())->handle();
Your new ChildA() is calling constructor from Base and you are not passing any arguments here.
You make mistake here: when you do new Base(10) your $var is not passed to new instance of new ChildA()
In your case I would use static parameter instead of constructor value
class Base {
protected static $var;
public function __construct(){
}
public static function setVar($var) {
self::$var = $var;
}
}
class ChildA extends Base {
public function handle(){
echo self::$var;
}
}
Base::setVar(10);
(new ChildA())->handle();

How to implement abstract properties in PHP? [duplicate]

Is there any way to define abstract class properties in PHP?
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
There is no such thing as defining a property.
You can only declare properties because they are containers of data reserved in memory on initialization.
A function on the other hand can be declared (types, name, parameters) without being defined (function body missing) and thus, can be made abstract.
"Abstract" only indicates that something was declared but not defined and therefore before using it, you need to define it or it becomes useless.
No, there is no way to enforce that with the compiler, you'd have to use run-time checks (say, in the constructor) for the $tablename variable, e.g.:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
To enforce this for all derived classes of Foo_Abstract you would have to make Foo_Abstract's constructor final, preventing overriding.
You could declare an abstract getter instead:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
Depending on the context of the property, if I want to force declaration of an abstract class property in an extended class, I like to use a constant with the static keyword for the property in the abstract object constructor or setter/getter methods. You can optionally use final to prevent the method from being overridden in extended classes.
Example: https://3v4l.org/WH5Xl
abstract class AbstractFoo
{
public $bar;
final public function __construct()
{
$this->bar = static::BAR;
}
}
class Foo extends AbstractFoo
{
//const BAR = 'foobar'; //uncomment to prevent exception
}
$foo = new Foo();
//Fatal Error: Undefined class constant 'BAR'
However, the extended class overrides the parent class properties and methods if redefined.
For example; if a property is declared as protected in the parent and redefined as public in the extended class, the resulting property is public. Otherwise, if the property is declared private in the parent it will remain private and not available to the extended class.
http://www.php.net//manual/en/language.oop5.static.php
As stated above, there is no such exact definition.
I, however, use this simple workaround to force the child class to define the "abstract" property:
abstract class Father
{
public $name;
abstract protected function setName(); // now every child class must declare this
// function and thus declare the property
public function __construct()
{
$this->setName();
}
}
class Son extends Father
{
protected function setName()
{
$this->name = "son";
}
function __construct(){
parent::__construct();
}
}
The need for abstract properties can indicate design problems. While many of answers implement kind of Template method pattern and it works, it always looks kind of strange.
Let's take a look at the original example:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
To mark something abstract is to indicate it a must-have thing. Well, a must-have value (in this case) is a required dependency, so it should be passed to the constructor during instantiation:
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Then if you actually want a more concrete named class you can inherit like so:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
This can be useful if you use DI container and have to pass different tables for different objects.
I've asked myself the same question today, and I'd like to add my two cents.
The reason we would like abstract properties is to make sure that subclasses define them and throw exceptions when they don't. In my specific case, I needed something that could work with statically.
Ideally I would like something like this:
abstract class A {
abstract protected static $prop;
}
class B extends A {
protected static $prop = 'B prop'; // $prop defined, B loads successfully
}
class C extends A {
// throws an exception when loading C for the first time because $prop
// is not defined.
}
I ended up with this implementation
abstract class A
{
// no $prop definition in A!
public static final function getProp()
{
return static::$prop;
}
}
class B extends A
{
protected static $prop = 'B prop';
}
class C extends A
{
}
As you can see, in A I don't define $prop, but I use it in a static getter. Therefore, the following code works
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
In C, on the other hand, I don't define $prop, so I get exceptions:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
I must call the getProp() method to get the exception and I can't get it on class loading, but it is quite close to the desired behavior, at least in my case.
I define getProp() as final to avoid that some smart guy (aka myself in 6 months) is tempted to do
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
As you could have found out by just testing your code:
Fatal error: Properties cannot be declared abstract in ... on line 3
No, there is not. Properties cannot be declared abstract in PHP.
However you can implement a getter/setter function abstract, this might be what you're looking for.
Properties aren't implemented (especially public properties), they just exist (or not):
$foo = new Foo;
$foo->publicProperty = 'Bar';
PHP 7 makes it quite a bit easier for making abstract "properties". Just as above, you will make them by creating abstract functions, but with PHP 7 you can define the return type for that function, which makes things a lot easier when you're building a base class that anyone can extend.
<?php
abstract class FooBase {
abstract public function FooProp(): string;
abstract public function BarProp(): BarClass;
public function foo() {
return $this->FooProp();
}
public function bar() {
return $this->BarProp()->name();
}
}
class BarClass {
public function name() {
return 'Bar!';
}
}
class FooClass extends FooBase {
public function FooProp(): string {
return 'Foo!';
}
public function BarProp(): BarClass {
// This would not work:
// return 'not working';
// But this will!
return new BarClass();
}
}
$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;
if tablename value will never change during the object's lifetime, following will be a simple yet safe implementation.
abstract class Foo_Abstract {
abstract protected function getTablename();
public function showTableName()
{
echo 'my table name is '.$this->getTablename();
}
}
class Foo extends Foo_Abstract {
//Foo must 'implement' getTablename()
protected function getTablename()
{
return 'users';
}
}
the key here is that the string value 'users' is specified and returned directly in getTablename() in child class implementation. The function mimics a "readonly" property.
This is fairly similar to a solution posted earlier on which uses an additional variable. I also like Marco's solution though it can be a bit more complicated.
Just define the property in the base class without assigning it a (default) value.
Getting the property value without redefining it with a default value or assigning it a value will throw an Error.
<?php
class Base {
protected string $name;
public function i_am() : string {
return $this->name;
}
}
class Wrong extends Base {
...
}
class Good extends Base {
protected string $name = 'Somebody';
}
$test = new Good();
echo $test->i_am(), '<br>'; // Will show "Nobody"
$test = new Wrong();
echo $test->i_am(), '<br>'; // Will throw an Error:
// Error: Typed property Base::$name must not be accessed before initialization in ....
?>
You can define a static property in an abstract class.
<?php
abstract class Foo {
private static $bar = "1234";
public static function func() {
echo self::$bar;
}
}
Foo::func(); // It will be printed 1234
Too late to answer the question, but you may use the difference between self and static as follows
<?php
class A { // Base Class
protected static $name = 'ClassA';
public static function getSelfName() {
return self::$name;
}
public static function getStaticName() {
return static::$name;
}
}
class B extends A {
protected static $name = 'ClassB';
}
echo A::getSelfName(); // ClassA
echo A::getStaticName(); // ClassA
echo B::getSelfName(); // ClassA
echo B::getStaticName(); // ClassB

PHP Keep parent class properties when using a child class constructor

I have a parent class Database as below
class Database{
/** #var Object Filemaker object across the application*/
protected $fm = null;
public function __construct(){
if($this->fm == null){
$this->_openConnection();
}
}
/**
* Function to open connection to FileMaker
* #return null
*/
private function _openConnection(){
$this->fm = new FileMaker();
}
}
and one of my child class is
class Login extends Database{
public function validate(){
// $this->fm is accessed here with no constructors
print_r($this->fm);
}
}
I'm creating object by,
$login = new Login();
$login->validate();
Note that in this Login class, I don't have any constructor. So at the line $login = new Login(); it is calling the Database class constructor and it calls the _openConnection(). This is fine
This is my another child class:
class PouleManipulation extends Database{
private $year;
public function __construct(){
// $this->fm is only accessed if I call parent class constructor
parent::__construct();
$year = '2015';
}
public function processQueue(){
$this->fm->perform();
}
}
and
$pm = new PouleManipulation();
$pm->processQueue();
Now, when I call the $pm = new PouleManipulation();, note that I have a constructor for this class. If I access $this->fm, then it is throwing an undefined error. I googled and found that,
Inside this class constructor I need to use parent::__construct(); to access the parent class properties. If I didn't use parent::__construct(); then the parent class object is not accessible when you are having a constructor in child class
Now, the problem is in the Database class $fm is null and it again calls the _openConnection(). I have totally 10 classes extending Database class and whichever classes having the constructor it calls the _openConnection().
I need the $this->fm to be accessed across all the child classes with calling the _openConnection() only one first time and need the $this->fm in all classes well. how do you achieve this?
Let's clarify a bit. The properties are set on an object level. This means that everytime you instantiate a class creating an object, this object is new and has its own properties different from the others you instantiated (they may have the same value, but they're stored in different places and they're independent).
Example:
class Database {
public $fm = "Value";
}
class Login extends Database {}
class PouleManipulation extends Database {}
$a = new Login();
$b = new PouleManipulation();
$b->fm = "New Value";
echo $a->fm; // prints "Value"
echo $b->fm; // prints "New Value"
What you're looking for is a static property, which is set at a class level. It's better if you access it through a static method. Here's a quick and dirty example, but you'd better use getters and setters.
class Database {
public static $fm = "Value";
}
class Login extends Database {}
class PouleManipulation extends Database {}
$a = new Login();
$b = new PouleManipulation();
$b::$fm = "New Value";
echo $a::$fm; // prints "New Value"
echo $b::$fm; // prints "New Value"
Edit:
I used the code you provided to explain how to achieve what you want, but I want to highlight what Ryan Vincent wrote in a comment: Login class should use (not extend) a Database class

Difference between constructor and unified constructor in php

Is there any difference in constructor or unified constructor. I have a class which have a constructor and a unified constructor. When i intialise object of the class then unified constructor call why not normal constructor.
<?php
class test {
function __construct() {
print "In BaseClass constructor\n";
}
function test() {
echo 'Class loeaded';
}
}
$obj = new test();
?>
OUTPUI
In BaseClass constructor
PHP Version 5.2.6
Since PHP 5, the best way to declare a constructor method in a class is to use the standardized
__construct()
So if you have
class myA {
public function __construct() {
echo "hello construct myA";
}
public function myA() {
echo "hello basic way myA";
}
}
$myA = new myA();
It will echo
hello construct myA
because the __constructor() has priority
But, for compatibility reason, the old way may work if there is not __construct() method.
And:
class myA {
//public function __construct() {
// echo "hello construct A";
//}
public function myA() {
echo "hello basic way myA";
}
}
$myA = new myA();
will give you:
hello basic way myA
In all cases, I advise you to use __construct()
the old constructor (method with class name) is only there for compatibility reasons. so if you have the new constructor (__construct) in the class php won't bother to call the old one.
edit
interesting note is that calling parent::__construct when the class being extended has only the old constructor type will still work (it becomes like a alias to the real constructor)
Classes which have a constructor method call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.
Read documentation of constructor (link)
Instances of classes are created using the new keyword. What happens during the new call is that a new object is allocated with its own copies of the properties defined in the class you requested, and then the constructor of the object is called in case one was defined. The constructor is a method named __construct(), which is automatically called by the new keyword after creating the object. It is usually used to automatically perform various initializations such as property initializations. Constructors can also accept arguments, in which case, when the new statement is written, you also need to send the constructor the function parameters in between the parentheses.
In PHP 4, instead of using __construct() as the constructor’s name, you had to define a method with the classes’ names, like C++. This still works with PHP 5, but you should use the new unified constructor naming convention for new applications.
We could pass the names of the people on the new line in the following class example :
class Person {
function __construct($name)
{
$this->name = $name;
}
function getName()
{
return $this->name;
}
private $name;
};
$judy = new Person("Judy") . "\n";
$joe = new Person("Joe") . "\n";
print $judy->getName();
print $joe->getName();
Tip: Because a constructor cannot return a value, the most common practice for raising an error from within the constructor is by throwing an exception.
According to documentation As of PHP 5.3.3, methods with the same name as that of class will no longer be treated as constructor .. They are like regular methods ...
<?php
class myclass
{
public function myclass()
{
// treated as constructor in PHP 5.3.0-5.3.2
// treated as regular method as of PHP 5.3.3
}
}
?>
`
For backwards compatibility, if PHP 5 cannot find a __construct() function for a given class, and the class did not inherit one from a parent class, it will search for the old-style constructor function, by the name of the class.
It means that there if you have constructor myclass() and __construct .. then __construct will be searched for first and taken as constructor instead of myclass()
class myclass
{
public function myclass()
{
echo 'hello';
}
function __construct() {
print "new constructor"
}
}
$obj = new myclass(); // will echo new constructor

Categories