Class visibility (private) inside __construct parameters - is this a valid php code? - php

I'm looking at some one else's code that I do not understand. This is example of class:
class ImageService
{
public function __construct(
private ImageTransformer $imageTransformer,
private PostService $postService
) {
}
// other methods here
}
IDE is showing errors under _construct due to "private" inside params.
I know that in PHP 8 there are union types, but I cannot find any info about using "private" or "public" not inside class but inside constructor, and what this supposed to do. I thought that this is typo, and it supposed to be inside class, but multiple classes are created like this. Is this a valid code or someone does not know what is he doing?

This is new in PHP 8.0.0 and is called Constructor Promotion.
As of PHP 8.0.0, constructor parameters may also be promoted to correspond to an object property. It is very common for constructor parameters to be assigned to a property in the constructor but otherwise not operated upon. Constructor promotion provides a short-hand for that use case.
If you execute that code you will see. You should check to see if your IDE has been updated to support PHP 8.0.0.

Related

Why is the last method with same name of the namespaced class not considered a constructor in PHP 5.3.3?

The history of PHP says that the older versions of PHP use the class name as a method for a constructor for the same class.
The PHP 5.3.3 documentation says that:
Methods with the same name as the last element of a
namespaced class name will no longer be treated as constructor. This
change doesn't affect non-namespaced classes.
Example:
namespace A;
class classOne{
public function methodOne(){}
public function methodTwo(){}
public function classOne(){} //Constructor
}
class classTwo{
public function methodOne(){}
public function methodTwo(){}
public function classTwo(){} //Constructor
}
So according to this documentation, classOne() and classTwo() are not considered as constructors? What is the reasoning behind this? Can anyone tell me?
Reference Link
Regarding why this rule applies for namespaced classes vs. non-namespaced, PHP 5.3 introduced namespaces. If you were upgrading from an older PHP version, you would have non-namespaced classes to look after, potentially using the old style way of creating constructors.
They want to enforce that you are developing within modern PHP principles and that you are also using the new conventions. PHP at this point is committed to removing old-style constructors completely, which we have seen as they are deprecated in PHP 7 and will be removed in a future version.
Finally, the reasoning behind dropping this convention altogether is that it is more error-prone. Using the DRY principles, if one was to change a class name while refactoring, forgetting to change the name of the constructor, it could have subtle and not-so-subtle repercussions.
If you are extending a class and want to call its constructor, it is also more error-prone if their parent class's name changes. For further reading:
https://stackoverflow.com/a/29794401/823549
and
https://stackoverflow.com/a/217876/823549.

Missing method implementations in PHP project

Recently I got someones PHP site project (and this someone don't wan't to help me), so I have to understand his code. And maybe my answer would be stupid, but...
But there's some methods before class, that are doxumented as in example:
namespace Base\Classes;
/**
* #method int method1()
* #method $this method2(int $parameter)
*/
class SomeClass extends ParentClass
{
public $_s_a = false;
public $_user_roles = [];
public function SomeClassMethod() {
somethingDone();
}
}
And as you can see in this example, these documented methods are not implemented in defined class. But what my question is about - this methods are called from another classes and templates. And PHPStorm (my IDE) connects this documentation lines with calls, and ctrl+B leads from between references. But I can't find exact implementation of this methods. They cannot be found in parent classes, they are not in this file. And I thought maybe this is some syntax sugar that I'm not familiar with. Am I right? Or there something I'm missing, and all implementations somewhere in another place? (search by method name in folder gives nothing for me)
PHP has a few magic methods, and one of them is __call().
When you have an object that implements __call() (by itself or by one of the parent classes), you may call an inaccessible method on it, and the __call() method will be called instead. This happens, for example, when you call a private method from the outside, or when you call a method that was not defined in code.
When you use such calls to inaccessible methods, IDEs will most likely show a warning that the method does not exist, although the code itself will probably work at runtime. These warnings are quite annoying, so you can add a #method tag to your class, and the IDE will know that this method exists, and will not show a warning.
So, to support the code that you got from someone, take a look at the __call() method implementation. Be aware that this method may be implemented in one of the parent classes, so check them out as well.

need help understanding the declaration of class properties

i was following an ACL tut. which has used this piece of code.
class ACL
{
var $perms = array();
var $userID = 0;
var $userRoles = array();
function __constructor($userID = '')
{
}
}
however i am unable to understand some of the above declarations.
a) the class property is declared starting with var keyword in the above class, in data encapsulation is it not necessary we use public,private, or protected keywords before the declaration of the property.? is the above method meant for PHP4 ? or will it work for php5 too?
b) my IDE(Panic Coda). takes __construct as the correct syntax for constructor. the above code has used __constructor . which one is correct ? to my knowledge in PHP4 the constructor name should be the same as class name if that is the case then is __construct and __constructor one and the same in PHP5?
thank you
a) The var keyword is indeed probably meant for PHP 4 compatibility. var is equivalent with PHP 5's public. It will work in PHP 5 as well, but seeing as PHP 4's time has passed, it is safe to move on to public, private, and protected.
b) __construct, or the name of the class for a PHP 4 compatible declaration, is the only correct way. __constructor() will not declare a constructor method.
In PHP4 all members and methods are static and public. var is definitely PHP4 syntax. In PHP5 you should use public, private and protected.
__construct() is correct method name for constructor. Since PHP 5.3 method that has the same name as the class is no longer treated as a constructor - it's just a regular method.
You should definitely find an up to date tutorial.
This example class has mix of PHP4 and PHP5.
Variable declarations have used PHP4 syntax and it is 100% OK with PHP5 too.
In PHP5, you can declare member variables as private, public or protected.
Even PHP5 functions can be private, public or protected.
But these accessor types are not compatible with PHP4.
Class constructor have used PHP5 syntax, but it's not compatible with PHP4.
Since you're a learner, please do adhere to PHP's naming convention, name the script file containing classes using class name. And don't use more than one class inside the same script file. All PHP files must be ended up with the extension .php to ensure security.
Further you can have static methods inside classes (don't mix static and dynamic methods inside the same class), and they can be invoked without creating objects such as Http::DoPost(...). But then $this cannot be used inside static methods.

is this a php bug: subclasses must declare private methods with the same signature as in parent class

I am using php 5.3, and yes, there is a bug open for that, but some think this is not a bug, and this makes me wonder.
abstract class A{
private function bobo(array $in){
//do something
}
}
class B extends A{
private function bobo($shmoo,$shmaa){
//do something
}
}
This throws an error. Shouldn't inheritance ignore private methods?!
'Declaration of B::bobo() should be
compatible with that of A::bobo()'
Note that the bug report is slightly off, as PHP will log this message any time you have an error level of E_STRICT (or, more recently, regardless of your error level provided that you've set a custom error handler).
PHP's visibility rules clearly demonstrate that a child lacks the ability to see its parent's private members, which I doubt is all that surprising to anyone. If the child can't see its parent's methods, I don't understand how it can have a duty to obey their definitions.
I personally think that the bug being marked as bogus without any explanation of why it wasn't a real flaw (since it's non-obvious and I couldn't find any mention of it in the documentation) is a bit wrong, but yeah. That aside, I'm of the opinion line 2669 in zend_compile.c should actually read as follows:
} else if (child->prototype &&
(EG(error_reporting) & E_STRICT || EG(user_error_handler))) {
...which would avoid the error popping up when the parent's method was marked private. Given that you always have the option not logging E_STRICT though, and it doesn't really negatively impact anything, I suppose it's not really a big deal. I definitely don't see how it could have been intentional, but I'm not a PHP engine developer either.
I think there are two possibilities here. Either it's a bug or the documentation on PHP.net/manual is incorrect. Here are three sections of the PHP manual. First on inheritance:
Object Inheritance
Inheritance is a well-established
programming principle, and PHP makes
use of this principle in its object
model. This principle will affect the
way many classes and objects relate to
one another.
For example, when you extend a class,
the subclass inherits all of the
public and protected methods from the
parent class. Unless a class overrides
those methods, they will retain their
original functionality.
This is useful for defining and
abstracting functionality, and permits
the implementation of additional
functionality in similar objects
without the need to reimplement all of
the shared functionality.
And on abstract classes:
Class Abstraction
PHP 5 introduces abstract classes and methods. It is not allowed to create an instance
of a class that has been defined as abstract. Any class that contains at least one
abstract method must also be abstract. Methods defined as abstract simply declare the
method's signature they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's
class declaration must be defined by the child; additionally, these methods must be
defined with the same (or a less restricted) visibility. For example, if the abstract
method is defined as protected, the function implementation must be defined as either
protected or public, but not private.
Finally, interfaces
Object Interfaces
Object interfaces allow you to create code which specifies which methods a class must
implement, without having to define how these methods are handled.
Interfaces are defined using the interface keyword, in the same way as a standard class,
but without any of the methods having their contents defined.
All methods declared in an interface must be public, this is the nature of an interface.
Suffice it to say: there is nothing in the documentation that mentions inheritance of private methods. If there is a relationship between the parent and child method signatures, then it is not documented and the bug report should at least show someone that the documentation needs to be updated (if the decision to have this behavior is intentional). And if there was not supposed to be a relationship, then well, it's a real bug.
That's my opinion...
In the bug report when you remove the interface there isn't an error.
That makes it "more" strange behavior because the interface is just empty.
I guess this is a design decision of the language. The Java language developers decided that this should be possible.
Private methods should certainly not be ignored by inheritance, consider for example Template method pattern where you may override behavior of a function in a derived class, but the parent class can still call that function
public class Parent {
public final function doThings() {
$this->initialize();
$this->customStuff();
$this->cleanup();
}
private final function initialize() {
// initialize processing
}
private final function cleanup() {
// cleanup processing
}
private function customStuff() {
// parent specific processing
}
}
public class Derived extends Parent {
private function customStuff() {
parent::customStuff();
// + derived class specific processing
}
}
Calling doThings method on Derived class instance will do parent specific processing, but because of possibility to override private methods it is still possible to take advantage of the extension point provided by the non-final parent class customStuff method.
EDIT: Also PHP method signature consists of only the method name as you can define a method taking zero parameters and still call it with multiple parameters. Function can then access the arguments using func_get_args function.

__construct() vs SameAsClassName() for constructor in PHP

Is there any advantage to using __construct() instead of the class's name for a constructor in PHP?
Example (__construct):
class Foo {
function __construct(){
//do stuff
}
}
Example (named):
class Foo {
function Foo(){
//do stuff
}
}
Having the __construct method (first example) is possible since PHP 5.
Having a method with the same name as the class as constructor (second example) is possible from PHP version 4 until version 7.
I agree with gizmo, the advantage is so you don't have to rename it if you rename your class. DRY.
Similarly, if you have a child class you can call
parent::__construct()
to call the parent constructor. If further down the track you change the class the child class inherits from, you don't have to change the construct call to the parent.
It seems like a small thing, but missing changing the constructor call name to your parents classes could create subtle (and not so subtle) bugs.
For example, if you inserted a class into your heirachy, but forgot to change the constructor calls, you could started calling constructors of grandparents instead of parents. This could often cause undesirable results which might be difficult to notice.
Also note that
As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor. This change doesn't affect non-namespaced classes.
Source: http://php.net/manual/en/language.oop5.decon.php
__construct was introduced in PHP5. It is the way you are supposed to do it now. I am not aware of any advantages per se, though.
From the PHP manual:
For backwards compatibility, if PHP 5 cannot find a __construct() function for a given class, it will search for the old-style constructor function, by the name of the class. Effectively, it means that the only case that would have compatibility issues is if the class had a method named __construct() which was used for different semantics
If you're on PHP5 I would recommend using __construct to avoid making PHP look elsewhere.
The main advantage I see for __construct, is that you don't have to rename your constructor if you change your class name.
Today, the accepted answer is obsolete.
Renaming classes is bad practice: you have to remember what and where to rename everytime you upgrade to newer version. Sometimes (like using Reflection or complex dependence structure) it can be impossible without radical refactoring. And this is accidental complexity you want to avoid. That's why namespaces were introduced into PHP. Java, C++ or C# don't use __construct, they use named constructor and there's no issue with them.
As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor. This change doesn't affect non-namespaced classes.
Example
namespace Foo;
class Test {
var $a = 3;
function Test($a) {
$this->a = $a;
}
function getA() {
return $this->a;
}
}
$test = new Test(4);
echo $test->getA(); // 3, Test is not a constructor, just ordinary function
Note that named constructors are not deprecated (PHP 5.5 today). However, you can't predict that your class won't be used in namespace, therefore __construct should be preffered.
Clarification about the bad practice mentioned above (for Dennis)
Somewhere in your code you could use ReflectionClass::getName(); when you rename the class, you need to remember where you used Reflection and check if the getName() result is still consistent in your app. The more you need to remember something specific, the more likely something is forgotten which results in bugs in the app.
The parents can't have control about all the classes in the world which depends on them. If allow_url_include is enabled, some other web might be using the class from your server, which may crash if you rename some class. It is even worse in compiled languages mentioned above: the library can be copied and bundled in other code.
There is no reason why to rename class:
if the class name conflicts, use namespaces
if the class responsibility shifts, derive some other class instead
In PHP classes in namespace, the method with the same name should be avoided anyway: intuitively it should produce an object created the class; if it does something else, why to give it the same name? It should be a constructor and nothing else. The main issue is that the behavior of such a method depends on namespace usage.
There is no issue with __construct constructors in PHP. But it wasn't the smartest idea to alter the named constructors.
The best advantage of using __contruct() instead of ClassName() is when extending classes. It is much easier to call parent::__construct() instead of parent::ClassName(), as it is reusable among classes and the parent can be changed easily.
In your example Foo::Foo is sometimes called a PHP 4 or old-style constructor because it comes from the days of PHP 4:
class Foo {
// PHP 4 constructor
function Foo(){
//do stuff
}
}
PHP 4 constructors will be deprecated but not removed in PHP 7. They will be no longer be considered as constructors in any situation in PHP 8. Future compatibility is definitely a big reason to not use this feature.
Well it has been a few years since this question was asked, but I think I have to answer this one still, because things has changed and for readers in the future I want to keep the information up to date!
So in php-7 they will remove the option to create the constructor as a function with the same name as the class. If you still do it you will get a E_DEPRECATED.
You can read more about this proposal (the proposal is accepted) here:
https://wiki.php.net/rfc/remove_php4_constructors
And a quote from there:
PHP 7 will emit E_DEPRECATED whenever a PHP 4 constructor is defined. When the method name matches the class name, the class is not in a namespace, and a PHP 5 constructor (__construct) is not present then an E_DEPRECATED will be emitted. PHP 8 will stop emitting E_DEPRECATED and the methods will not be recognized as constructors.
Also you won't get a E_STRICT in php-7 if you define a method with the same name as the class AND a __construct().
You can see this also here:
PHP 7 will also stop emitting E_STRICT when a method with the same name as the class is present as well as __construct.
So I would recommend you to use __construct(), since you will have less issues with this in the future.
In PHP 5 the advantage would be that performance would be better. It will look for a constructor by the name of __construct first and if it doesn't find that, it will look for constructors by the name of className. So if it finds a constructor by the name __construct it does not need to search for a constructor by the name className.
Forward compatibility. There's always a chance that legacy code that's left in the language for backwards compatibility's sake will be removed in a future version.
If there is methods __construct and SameAsClassName method then __construct will be executed, SameAsClassName method will be skipped.
I think that the main reason is that is the language convention.
You don't need to force a language to act like someone else.
I mean, in Objective-C you prefix the constructors with -init, for example. You can make your own constructor using your class name but why? Are ther some reason to use this schema instead of the language convention?

Categories