init() method :
public function init()
{
}
__construct() method:
public function __construct()
{
}
So, what's the differentce between them, and which should be used?
init() is the method of any object that extends from yii\base\Object (and the majority of objects extends from it).
From official docs:
Besides the property feature, Object also introduces an important
object initialization life cycle. In particular, creating a new
instance of Object or its derived class will involve the following
life cycles sequentially:
the class constructor is invoked;
object properties are initialized according to the given configuration;
the init() method is invoked.
In the above, both Step 2 and 3 occur at the end of the class
constructor. It is recommended that you perform object initialization
in the init() method because at that stage, the object configuration
is already applied.
It's recommended to use init(), you can even see it from source code and the extensions, but in some cases, you can use __construct(). There are some recommendations to implement that, you can find it on the same page in official docs here.
__constuct is a native PHP language feature, you can read more info about that in PHP official docs in this section.
Related
The environment I have set up is using MAMP with php version 7.2.14 and PhpStorm. Based on the PHP: Trait - Manual,
traits are mechanisms for code reuse in single inheritance languages...
I have created a ValidationHelper.php trait to help validate form data. Pear has validation functionality like email($email_addr) that accepts an email address to validate. Calling this class statically appears to no longer be ideal, so I am attempting to instantiate it. Php throws an error if you attempt to initialize a property with an object, therefore, I added a constructor to instantiate the object. The PHP: Trait - Manual also states that
It is not possible to instantiate a Trait on its own.
On its own being the key phrase for ambiguity. With that being said, how would you add a constructor that initializes some property, or instantiate an object using a trait?
Can Traits have properties...Constructors is where I read that traits can have these members. I do see that they included a constructor, but not 100% sure how it works.
include_once 'Pear/Validate.php';
trait ValidationHelper
{
protected $validate;
public function __constructor(){
$this->validate = new Validate();
}
public function validate_email($email){
return $this->validate->email($email); //$validate is null
}
}
#Kevin Wiggins
It seems that there is a misunderstanding.
As mentioned in the first answer of Can Traits have properties...Constructors
Traits can have a constructor and destructor but they are not for the trait itself, they are for the class which uses the trait. Therefore I wouldn't recommend to do this.
This means that if you declare a constructor / destructor in the included trait this function acts as if it was declare in the class itself. So this will only work, if the only class vars added to the class are defined in the trait.
A trait itself can not be instantiate at all. It is only a grouped piece of code which is injected in a class and therefore behave as it was written in this class itself.
Instead of trying to instantiate an object within a trait, instantiate it within your hierarchal system.
include_once 'Pear/Validate.php';
class My_Object_Class
{
protected $validate;
public function __construct(){
$this->validate = new Validate();
}
}
Consider the following code:
abstract class ExampleClass
{
public static function regularStaticFunction()
{
return static::abstractStaticFunction();
}
abstract protected static function abstractStaticFunction();
}
ExampleClass::regularStaticFunction();
The PhpStorm IDE puts a warning on the declaration of abstractStaticFunction that reads:
PHP Strict Standards: Static function 'abstractStaticFunction' should not be abstract.
Static function should not be abstract.
However, PHP continues program execution when parsing this class and outputs the following:
PHP Strict standards: Static function ExampleClass::abstractStaticFunction() should not be abstract in php shell code on line 7
It seems to me that because PHP allows static function calls on abstract classes, defining an abstract static function on an abstract class should not be possible.
Why are abstract static functions allowed in PHP by the interpreter, when they are nonsensical?
This is good explanation from this answer by Mark Amery:
PHP bug report 53081, called
for the warning to be dropped since the addition of the
static::foo() construct had made abstract static methods reasonable
and useful. Rasmus Lerdorf (creator of PHP) starts off by labelling
the request as bogus and goes through a long chain of bad reasoning to
try to justify the warning. Then, finally, this exchange takes place:
Giorgio
i know, but:
abstract class cA
{
//static function A(){self::B();} error, undefined method
static function A(){static::B();} // good
abstract static function B();
}
class cB extends cA
{
static function B(){echo "ok";}
}
cB::A();
Rasmus
Right, that is exactly how it should work.
Giorgio
but it is not allowed :(
Rasmus
What's not allowed?
abstract class cA {
static function A(){static::B();}
abstract static function B();
}
class cB extends cA {
static function B(){echo "ok";}
}
cB::A();
This works fine. You obviously can't call self::B(), but static::B()
is fine.
The claim by Rasmus that the code in his example "works fine" is
false; as you know, it throws a strict mode warning. I guess he was
testing without strict mode turned on. Regardless, a confused Rasmus
left the request erroneously closed as "bogus".
And that's why the warning is still in the language. This may not be
an entirely satisfying explanation - you probably came here hoping
there was a rational justification of the warning. Unfortunately, in
the real world, sometimes choices are born from mundane mistakes and
bad reasoning rather than from rational decision-making. This is
simply one of those times.
Luckily, the estimable Nikita Popov has removed the warning from the
language in PHP 7 as part of PHP RFC: Reclassify E_STRICT
notices. Ultimately,
sanity has prevailed, and once PHP 7 is released we can all happily
use abstract static without receiving this silly warning.
Abstract static functions aren't nonsensical! In fact, they enable some designs that are, to my sensibilities, simpler and cleaner than what I'd have to resort to in a language that doesn't have them, like Java or C#.
Let's take an example. Suppose I'm writing some sort of mundane enterprisey business application that needs to synchronise some business objects between two APIs. These APIs have object models that can be mapped to each other, but use different names, and different serialisation formats.
In PHP, thanks to abstract static methods, I can define an abstract base class for these business object types that looks like this...
abstract class ApiObject {
/** The REST resource URL for this object type in the Foo API. */
abstract static function fooApiResourceUrl();
/** The REST resource URL for this object type in the Bar API. */
abstract static function barApiResourceUrl();
/** Given an XML response from the Foo API representing an object of this
type, construct an instance. */
abstract static function fromFooXml($xml);
/** Given a JSON response from the Bar API representing an object of this
type, construct an instance. */
abstract static function fromBarJson($json);
/** Serialize this object in the XML format that the Foo API understands */
abstract function toFooXml();
/** Serialize this object as JSON that the Bar API understands */
abstract function toBarJson();
}
... and then every concrete subclass I create will be guaranteed to provide all the information needed to fetch it from either of the two APIs and deserialise it, or to serialize it and send it to either API. Then, later, I can write some code like this:
// Ensure that all instances of these types that exist in the Foo API also
// exist in the Bar API:
$classesToSync = ['Widget', 'Frobnicator', 'Lead', 'Invoice'];
foreach ($classesToSync as $apiObjectClass) {
$fooObjXmls = httpGetRequest($apiObjectClass::fooApiResourceUrl());
foreach ($fooObjXmls as $fooObjXml) {
$fooObj = $apiObjectClass::fromFooXml($fooObjXml);
$json = $fooObj->toBarJson();
httpPutRequest($apiObjectClass::barApiResourceUrl(), $json);
}
}
Do I strictly need abstract static methods to write the program above? No; there are other patterns I could've used, like having every model class be paired with a corresponding factory class that is responsible for instantiating it from its JSON or XML representations. But such a design is more complicated than the one I've shown above.
The answer to why they're allowed, then, is simply that they are useful, since they enable some nice, simple patterns that wouldn't be possible without them. Of course, there are also arguments against them existing, one of which was given in the question - that it's ugly to expose abstract static methods on a class given that static methods on an abstract class are callable. I don't find such considerations particularly persuasive, but even if you do, there is still a tradeoff between them and the utility that abstract static methods provide, and the PHP maintainers have presumably weighed them and opted for the side of letting abstract static methods exist.
I have an abstract class like this
<?php
abstract class AbastractCreationCommand extends AbstactCommand {
protected $repository;
function handle($payload) {
$this->repository->create($payload);
}
}
class TagCreationCmd extends AbstractCreationCommand {
function __constructor() {
$this->repository = new TagRepository();
}
}
?>
Questions:
is there a way I could enforce the definition of the repository class in the subclasseses of the AbstractCreationCommand ?
Do I need to create a test for each subclass and call handle method or is another way to test all my code?
Answering number 1: You cannot enforce the creation of anything in subclasses from within your abstract class. At least not during instantiation. Everything in the subclass is optional.
However, your code in the abstract class could check whether or not the necessary objects have been defined when executing the part of the code that needs it, like this:
abstract class AbastractCreationCommand extends AbstactCommand {
protected $repository;
function handle($payload) {
if (!$this->repository instanceof TagRepository) {
throw new \InvalidArgumentException('Need a TagRepository');
}
$this->repository->create($payload);
}
}
However, is likely doing the complaining too late. And the reason may be because you are using inheritance instead of composition, or are inheriting the wrong things.
First of all, you are not doing dependency injection. Your subclass should not directly instantiate that TagRepository. This leads to problems testing your abstract class' code, as well as the subclass code, because you cannot provide a mock object instead. This severely limits the ability to test your code in isolation.
Also, the subclass cannot work without knowing very specifically how to inherit the abstract class beyond implementing any abstract functions. If both abstract and subclass come from you as the author, I would consider it to be ok to impose doing all things correctly on you. But if you expect other developers to inherit that abstract class (and your question sounds like this might be the background problem), then you shouldn't do this at all.
Abstract classes do provide some common functions to a set of subclasses through inheritance. But the same thing could be achieved if you put all the code into a non-abstract class and inject this class into independent ex-sub classes. They will call these common functions as public methods instead of private or protected, and the testing of the common code is also easier, because the methods are public.
Also note that you already have three levels of inheritance, which is nearing a uncomfortable level: AbstractCommand -> AbstractCreationCommand -> TagCreationCmd.
The problem is that everything you change in AbstractCommand has to be done with two levels of inheriting objects in mind. You cannot simply change a protected variable's name. You cannot simply add a protected (or public) variable without checking if any of the sub classes already has such a variable with the same name - unless you intend to share it.
The problems with maintaining code that is inherited is not with the classes at the end of the inheritance chain, but with these at the top. Just think about how many classes might be affected with different usage contexts: If you have AbstractCreationCommands, you will have AbstractDeletionCommands and AbstractChangeCommands and AbstractDoNothingCommands, and a plethora of concrete commands of all these kinds doing plenty of different stuff. Just imaging that on each level, you have four classes - this makes you having to maintain one base class, four inheriting classes, and four times four concrete classes - for a whopping 21 classes in total, all of them having to be tested, and likely none of them gaining any benefit from being an instanceof AbstractCommand.
Answering number 2: Yes, you have to test all subclasses - these are the ones that get instantiated and used. You should also test the abstract class' code in isolation. PHPUnit offers to instantiate an abstract class with the mock framework, so any abstract method would be mocked and could be configured. However, I always have a bad feeling when I use a mock as the real tested object because I am not really testing the pure code, but some combination of mock code and real code.
A possible way out is to create a test class that barely does anything beyond extending the abstract class, and work with this one.
1.Yes. I do the same when I need to force instantiation of some dependancies. And this way is support GRASP: Creator principle.
Another way to accomplish that with dependency injection, but this way broke Creator principle:
class TagCreationCmd extends AbstractCreationCommand {
function __constructor(TagRepositoryInterface $tagRepository) {
$this->repository = $tagRepository;
}
}
If follow 3 rules of TDD you should write test for each line of code. So the answer is yes.
Is there a way I could enforce the definition of the repository class in the subclasseses of the AbstractCreationCommand ?
I don't see the necessity tbh. If your AbstractCreationCommand needs a repo to work, add it as a constructor param. This doesn't enforce the repo to be injected because a subtype can override the constructor, but it should be abundantly clear that an AbstractCreationCommand subtype requires some sort of repo then, e.g.
abstract class AbstractCreationCommand extends AbstractCommand
{
private $repository;
public function __construct(Repository $repository)
{
$this->repository = $repository
}
protected function getRepository(): Repository
{
return $this->repository;
}
// …
You could also use a Template Method pattern to indicate that any subtype will utilize a repo by adding an abstract getter for the repo. The subtype will have to implement that method then. It's then up to the developer to decide on the implementation:
abstract class AbstractCreationCommand extends AbstractCommand
{
public function handle()
{
$this->getRepository()->create();
}
abstract function getRepository(): Repository;
// …
If you really must enforce it at creation level, you can set the abstract type's constructor to final protected and do any subtype creation in a static factory method, e.g.
abstract class AbstractCreationCommand extends AbstractCommand
{
private $repository;
final protected function __construct(Repository $repository)
{
$this->repository = $repository;
}
// …
This now prevents direct instantiation of any subtypes via new. Trying to new a subtype, will result in PHP Fatal error.
Instead the subtype must be created like this:
class TagCreationCommand extends AbstractCreationCommand
{
private $foo;
public static function create(Repository $repository, Foo $foo)
{
$command = new static ($repository);
$command->setFoo($foo);
return $command;
}
protected function setFoo(Foo $foo)
{
$this->foo = $foo;
}
// …
Then you'd call TagCreationCommand::create(new TagRepository, new Foo); to get a new instance. Since you cannot override the constructor and have to call the parent constructor from within the static create method, you effectively enforce a Repository now to be there. I added the Foo stuff only to illustrate how you'd use additional dependencies.
As you can hopefully see, this requires quite a lot of gymnastics compared to the much more lightweight previous two approaches that will basically result in the same outcome. After all, if there is no repo, the code will fail. And since you are using tests, this will get noticed. So why bother?
Do I need to create a test for each subclass and call handle method or is another way to test all my code?
If you are overriding the handle method, you should test that behavior in a concrete test class for that subtype.
If your subtypes do not override the handle method, you can create an AbstractCreationCommandTest and put a test for the handle method in there. However, if that is the case, I wonder why you need the AbstractCreationCommand to be abstract in the first place because then it sounds like you just need a CreationCommand.
Following Yan Burtovoy's suggestion, I would go even further and actually enforce a DI container
<?php
abstract class AbastractCreationCommand extends AbstactCommand {
protected $repository;
function __constructor(\DI\Container $container) {
$this->repository = $container->get('TagRepository');
}
function handle($payload) {
$this->repository->create($payload);
}
}
You should create tests for everything that is exposed to users of your library (that would be your application).
So, if you have a subclass that relies on handle() being called then you should write a test for that. Reason being that in 6 months someone might change the inheritance or overwrite the handle() method and change the initial expected behaviour.
I have created an interface meant to be implemented by users. The only real requirements I have is that the user's implement a Save() and a Load() method. Now save is simple, but the problem I'm having is with Load().
In order for Load() to be part of the interface it would need to be an instance method. But the nature of it is to return a new object that has been loaded from a database. This would imply that Load() needs to be defined as static. Static methods aren't enforced by the interface.
How can I require users who implement the interface to write their own code for Load()?
I'm using PHP5.4 which does require that constructor definitions are followed in subclasses, so one thought was to change this into an Abstract Class and define a constructor that takes an $id variable. If it's null we create a new object, if it's set then load the object. I'd much prefer to keep this setup as an interface. There also is some concern of ambiguity and some user implementing their constructor incorrectly. It's easier to document and describe what load() should do.
Why does "a method that returns an object" immediately mean that it has to be static? Static isn't a blanket "go-to" on object creation - it actually serves a specific purpose.
Anyway. There is nothing stopping you from defining an interface that requires the implementation of a static method, as follows, if your heart so desires :
interface MyTestInterface {
public static function Load();
}
class MyTest implements MyTestInterface {
public static function Load() {
echo "Test";
}
}
Fiddle: http://codepad.viper-7.com/xcoQZh
There is nothing stopping you from defining a method requiring implementation, either through an interface or an abstract class, when the method is static. However, consider the actual static use in this case....
I have recently started php and was wondering the differences between __construct() and having a method with the same name as the class?
Is there a reason for using it? All I can work out is it overrides the method named Foo or is it down to which you prefer?
E.g.
class Foo {
function Foo()
{
echo 'Foo stated<br>';
}
function __construct() {
echo 'Construct started<br>';
}
}
Thanks
Using __construct() is the newer PHP5 more OOP focused method to call a constructor. Using a method with the same name as the class is the old deprecated way to do it, and it will not function as a constructor as of PHP 5.3.3 for namespaced classes.
From the constructors and destructors page:
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.
Unlike with other methods, PHP will not generate an E_STRICT level error message when __construct() is overridden with different parameters than the parent __construct() method has.
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.
"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."
Source: http://www.php.net/manual/en/language.oop5.decon.php
Foo() is a php4 way(deprecated), __construct() is php5 way. php will first look for __construct, then, if it's not found, it will use Foo
Is there a reason for using it? All I
can work out is it overrides the
method named Foo or is it down to
which you prefer?
The benefits of __construct() become clearer when you involve renaming and inheritance. If you rename a class, you then have to rename it's constructor. No big deal, but if class B inherits from class A you could end up with:
class B extends A {
function B() {
parent::A();
}
}
It's much easier and more maintainable to do:
function __construct() {
parent::__construct();
}
Now when you rename class A, you don't have to remember to also change the parent calls in all its children. Granted, there's still renaming to do in your code but at least this is not one of them.
Having a method with the same name as the class, which is automatically called as a constructor is a throwback to PHP4, and is to be considered deprecated... in the latest development branch of PHP, it will be treated as a normal class method. __construct is the formally accepted constructor in PHP5, although in current releases it will fall back to the PHP4 method if __construct doesn't exist.