I was reading a short tutorial on Laravel here. Since I am not experienced in laravel or development in general I am wondering what this part of the code does exactly:
public function approve(): User
As it is seems to me, it is the same thing as just calling the model from inside the function like so:
App\User::
What is the difference in this two approches?
The first example you shared:
public function approve(): User
is simply a feature of PHP7 which allows you to use static type programming practices with PHP. Essentially this new function signature is telling you that this function needs to return a User type or it will throw a TypeError exception.
The second example you shared:
App\User::
is using what is called the Scope Resolution Operator(::) This operator allows you to call Class Level / Static Methods. In Laravel for example, that would be something like:
App\User::Find(1);
or
App\User::Where('id', 1);
and these differ from object level methods which would be called like so:
$user = new App\User();
$user->id = 1;
$user->save()
Notice the class instance uses the -> operator.
You can learn more about what I mentioned at the following links:
https://secure.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration
http://php.net/manual/en/language.oop5.paamayim-nekudotayim.php
https://laravel.com/docs/5.3/eloquent
Best of luck!
No, they're not the same. The first code is utilizing PHP 7's return type declerations.
It says that the method must return an instance of User class, or PHP will throw a TypeError for you. You can also specify the FQN of the class when defining return type declarations:
public function approve(): \App\User
It's specially useful when defining interfaces:
interface CommentsIterator extends Iterator {
function current(): Comment;
// This enforces that any implementation of current()
// method, must return an instance of Comment class.
}
See what other RFCs made their way into PHP 7:
https://secure.php.net/manual/en/migration70.new-features.php
And treat yourself with Jeffrey's screencasts on PHP 7 new features:
https://laracasts.com/series/php7-up-and-running/episodes/3
But the second code (App\User::whatever()) is just calling the static method whatever() of the App\User class. It has nothing to do with the first code example which enforces return types.
Related
I have an abstract class I am inheriting from:
abstract class Test
{
public function GetTests()
{
}
}
and I have a concrete that I have been using the abstract classes implementation for most of the time:
class Concrete extends Test
{
// No problemmos
}
I recently had to implement a different version of the GetTests method, and in fact I wanted to overwrite it as it's built into all of my routing:
class Concrete extends Test
{
public function GetTests( $newArgument )
{
// notice $newArgument
}
}
However I get this error message:
Declaration of Concrete::GetTests() should be compatible with Test::GetTests()
Apart from copying the entirety of the functions from the abstract class for this concrete, even though I only need to implement this one method differently... Is there a way of getting around this?
I do understand that I could have:
abstract class Test
{
abstract public function GetTests();
}
But this is why I am snookered, because I no longer have the ability to modify how the underlying Test class is implemented... doh!... Unless I really have to..
Thanks to all great answers...
I have decided to de snooker myself (it's going to hurt but it's going to be worth it) and I will instantiate the Test class inside the Concrete class, implement concrete versions of all the Test class methods, and then inside them just call the instantiated Test class... This means in the future (or indeed now) I can simply not call that feature...
For context:
/* no longer abstract */ class UnitOfWorkController
{
public function GetUnits()
{
// Implementation
return View::make(...);
}
}
and...
class SomethingController /* no longer extends the UnitOfWorkController */
{
private $unitOfWorkController;
public function __Construct()
{
$this->unitOfWorkController = new UnitOfWorkController();
}
public function GetUnits()
{
return $this->unitOfWorkController->GetUnits();
// or I could just implement my own junk
}
}
Your concrete subclass is in violation of the Liskov Substitution Principle, which to cut a long story short says that if an object of class X can be processed by a given piece of code, then every possible subclass of X must also be able to be processed by the same piece of code.
Say I wanted to make another subclass of Test and wanted to implement my own GetTests method. The base class method doesn't accept any arguments at all, so that suggests that, if my subclass is to be substitutable for its superclass, my implementation of that method cannot take any arguments either. If I give my implementation arguments, then it no longer conforms to the specification as laid down by the superclass.
If I have code that does:
$object = new Test;
$test -> GetTests ();
then I can't substitute my subclass of Test without also changing the calling code to pass in an argument. Likewise if I do change it, then I have another subclass of Test that doesn't require an argument for GetTests then the code would have to change again. In fact the same code simply can't be used as is with both subclasses without having to jump through some hoops to determine the actual class and using the appropriate calling convention which means needing to know things about the class I'm about to use that I shouldn't need to know.
PHP is less strict than most OO languages about subclass method signitures matching their superclass, but it will issue a warning if they don't match. The only way to fix the warning is to have all subclasses have the same method signatures as the superclass they inherit from.
Child methods must have the same signature as the same method in a parent class. This includes required parameters and their typecasting.
For example, a child class of the following method must also have one parameter, and the parameter must cast to the ArgumentType class or a child of thereof.
public function something(ArgumentType $Argument)
{
}
You can, however, make the parameter optional by setting it to null or any other value:
public function something(ArgumentType $Argument = null)
{
}
In this case, child methods may omit this parameter.
From the PHP docs, see http://php.net/manual/en/language.oop5.abstract.php:
[…] Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. For example, if the child class defines an optional argument, where the abstract method's signature does not, there is no conflict in the signature.
The method signature of Concrete::GetTests() has a variable while Test::GetTests() does not. Since you have already defined this method within Test, it is now being inherited. The inherited version is not compatible with your overridden version.
Here are your options:
Add $newArgument to the parameters list in Test::GetTests().
Remove $newArgument from the parameters list in Concrete::GetTests().
Rename Concrete::GetTests() to something else.
PHP does not support this, as the error message says. If you want to override the function, it has to have the same footprint, which in your case it doesn't
What you could do is use a magic method: http://php.net/manual/en/language.oop5.overloading.php#object.call
the parameter array is a separate entity, so you 'decide' in your code (which you can override) what to do with which parameter.
I wanted to link a blog I read about this, but couldn't find the one I was thinking of. There is this rather strangely formatted one, not sure if it any good, but it does touch on some of the issues.
You could obviously add the argument to the parent, but this is 'leaking' upwards. If other childeren want even more, you'd get a big party of random paramteres that all can be nulled.
In PHP laravel, we have codes like
$user = User::find(1);
var_dump($user->name);
I am not concerning how to use the find method, I am concerning why laravel use a static method? Shouldn't the use of static method make the method hard to test?
Will it be better if they designed using singleton?
e.g.
$user = User::getInstance()->find(1);
var_dump($user->name);
In fact, your example is very similar to what Laravel does behind the scene. When you do User::find(), you are actually asking for a new instance, either an instance of Collection or a QueryBuilder.
Illuminate\Database\Eloquent\Model (reference):
public static function find($id, $columns = array('*'))
{
if (is_array($id) && empty($id)) return new Collection;
$instance = new static;
return $instance->newQuery()->find($id, $columns);
}
As a side note, you'll also see another way of using static methods in Laravel e.g. Input::get(). These are called Facades.
Facades provide a "static" interface to classes that are available in the application's IoC container ... Laravel "facades" serve as "static proxies" to underlying classes in the IoC container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.
When a user references any static method on the ... facade, Laravel resolves the cache binding from the IoC container and runs the requested method (in this case, get) against that object.
You can read more about Larave's Facades at: http://laravel.com/docs/facades
Unnawut has a very good answer, however I felt it necessary to add in further explanation.
In your example
$user = User::find(1);
var_dump($user->name);
Laravel isn't using a static method, you are. Another way to do this which you are probably looking for is to use dependency injection, which Laravel makes very easy because it can be done automatically. So in whatever class you are using your User model in, you should be setting up something like this in the constructor...
public function __construct(User $user)
{
$this->user = $user;
}
And then you can modify your code to not use the static bindings.
$user = $this->user->find(1);
var_dump($user->name);
This would restrict the system from only having one User. Whilst the find method may be static, the User class will have other methods and properties that aren't, a likely example is in your example: $user->name
A method that does not rely upon any instance variables, I.e variables who's value is specific to the particular object instance, but instead provides generic functionality that applies to all instances, can, and probably should, be static. This is why the $this operator is illegal within static methods, as it can make no reference to a particular object instance.
#unnawut reference link to the source code of Model.php is no longer using static find function as the class has been refactored. The function that get called eventually is in Builder.php (source code),
The logic still same
- magic function `__callStatic` of Model class get called
- a new instance of Builder is created and return
- call the find method of the Builder instance
Model.php
public function newEloquentBuilder($query)
{
return new Builder($query);
}
Builder.php
public function find($id, $columns = ['*'])
{
if (is_array($id) || $id instanceof Arrayable) {
return $this->findMany($id, $columns);
}
return $this->whereKey($id)->first($columns);
}
Is there a way to create a mock class with PHPUnit which I can then create a new instance of by using its class name?
I have an interface which defines two methods. Something like:
interface FooInterface {
function getA();
function getB();
}
I then have another class which accepts a class name, creates an instance of that class, checks if it is an instance of what it expects (FooInterface) and then calls two methods on that class to get some information.
class FooInfo {
protected $a;
protected $b;
public function __construct($fooClass) {
$foo = new $fooClass;
if (!($foo instanceof FooInterface)) {
throw new \Exception();
}
$this->a = $foo->getA();
$this->b = $foo->getB();
}
}
I know how to mock an object just fine. The problem is, since this class accepts a class name, not an object (it is a part of a Manager which creates instances of the given class as needed), I can't use a normal mock object.
I tried to make a mock object then use that class name. It seems to create the object just fine, and even seems to have the functions I mocked out. However, it doesn't seem to follow the will($this->returnValue('myValue')) portion I set up later.
public function testConstruct()
{
$foo = $this->getMockForAbstractClass('Foo', array('getA', 'getB'));
$foo->expects($this->any())->method->('getA')->will($this->returnValue('a'));
$foo->expects($this->any())->method->('getB')->will($this->returnValue('b'));
$copyClass = get_class($foo);
$copy = new $copyClass();
// Passes
$this->assertTrue(method_exists($copy, 'getA');
// Fails, $copy->getA() returns null.
$this->assertEquals($copy->getA(), $foo->getA());
}
So, it does have the functions which were mocked, but they all return null.
Any ideas?
To use the new keyword in the constructor of a class is a rather bad habit exactly for the reasons you're experiencing now, even given your flexible use case.
Your test will not work because the mock you create will never be used, since your class will always create a real instance of the injected classname.
That said, what you want to do can be done with padraic's excellent mocking library mockery!
What you need is an 'instance mock':
$mock = \Mockery::mock('overload:MyNamespace\MyClass');
You can define your expectations on that mock which will be transferred to the real object, as soon as it is instantiated.
Integrating mockery with phpUnit is easy and well explained in the readme of the project.
And btw. each unit test should optimally make one assertion only!
The methods getA and getB return null because you have not specified what they should return. You did specify that for the abstract $foo by calling some methods. There's no way around that.
Since it's hard (if not impossible) to test the function, I would rewrite the code itself. Testing would be easy if your constructor would require a class instance rather than a class name. If you also must accept strings, you could write a few lines to check for a string input and create a class if a string is provided.
Am I missing something or there really is no support for generic object type hinting in PHP 5.x?
I find it really strange that hinting arrays is supported while hinting objects is not, at least not out of the box.
I'd like to have something like this:
function foo(object $o)
Just as we have:
function foo(array $o)
Example of possible use: methods of an objects collection class.
Workaround: using an interface "Object" implemented by all classes or extending all classes from a generic class "Object" and writing something like this:
function foo(Object $o)
Well, that just ain't cute.
Using stdClass as the type hint doesn't work:
Catchable fatal error: Argument 1
passed to c::add() must be an instance
of stdClass, instance of b given
Since type hinting should make the client code adapt to your API, your solution with accepting interfaces seems just about right.
Look at it this way: yourMethod(array $input) gives yourMethod() an array to use, thereby you know exactly which native functions that applies and can be used by yourMethod().
If you specify your method like: yourSecondMethod(yourInterface $input) you'd also know which methods that can be applied to $input since you know about/can lookup which set of rules that accompanies the interface yourInterface.
In your case, accepting any object seems wrong, because you don't have any way of knowing which methods to use on the input. Example:
function foo(Object $o) {
return $o->thisMethodMayOrMayNotExist();
}
(Not implying that syntax is valid)
No, it can't be done. I wasn't missing anything.
I feel your pain, but I can't find a way of doing it either.
Despite what a number of other posters have said, it makes perfect sense to want 'Object' type hinting; they just haven't considered a scenario that requires it.
I am trying to do some work with the reflection API, and because of that I don't care what class is passed to my function. All I care is that it's an object. I don't want an int, a float, a string or an array. I want an object. Given that reflection is now part of PHP, it definitely makes sense to have object type hinting.
You cannot just say "object" when type casting an object... you must define WHICH object you are expecting.
From: http://php.net/manual/en/language.oop5.typehinting.php
class MyClass
{
/**
* A test function
*
* First parameter must be an object of type OtherClass
*/
public function test(OtherClass $otherclass) {
echo $otherclass->var;
}
/**
* Another test function
*
* First parameter must be an array
*/
public function test_array(array $input_array) {
print_r($input_array);
}
}
// Another example class
class OtherClass {
public $var = 'Hello World';
}
The best way to enforce this would be to create a degenerate interface called Object. A degenerate interface means it has no defined methods.
interface Object {
// leave blank
}
Then in your base classes, you can implement Object.
class SomeBase implements Object {
// your implementation
}
You can now call your function as you wanted to
function myFunc (Object $obj);
myFunc($someBase);
If you pass any object which inherits from your Object interface, this type hint will pass. If you pass in an array, int, string etc, the type hint will fail.
Here's another example where it is required...
I've created a class to implement record locking. Records being one of a number of different object types. The locking class has several methods which require an object (the one to be locked) but don't care what type of object it is.
E.g.
public static function lockRecord($record, User $user, $timeout=null)
{
if(!is_object($record)) throw new \InvalidException("Argument 1 must be an object.");
$lock=new Lock();
$lock->setRecord($record);
$lock->setUser($user);
$lock->setTimeout($timeout);
$lock->activate();
return($lock);
}
You'll see that my solution was to use is_object() and throw an exception, but I'd far rather be able to do it with type hinting instead.
Ok, so not the end of the world, but I think it's a shame.
Objects in php are not subclasses of some StdClass or Object as it is in other OOP languages. So there is no way of type hinting the object. But I see your point because sometimes you want to make sure that the Object is being passed, so I guess the only way is to raise the issue manually.
public function yourFunction($object){
if(is_object($object)){
//TODO: do something
}else{
throw new InvalidArgumentException;
}
}
As of php 5.4 there is also a type hint callable.
See php manual http://php.net/manual/en/language.types.callable.php
Why would you want to hint object when you can hint an actual class name instead - this would be much more useful. Also remember that you can't hint int,float, bool, string or resource either.
public static function cloneObject($source)
{
if ($source === null)
{
return null;
}
return unserialize(serialize($source));
}
This is where you would need it.
Since PHP 7.2 you can finally declare the way you wanted:
function functionName(object $someObjectVariable)
See the table named "Valid types" at this page:
https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration
After reading this description of late static binding (LSB) I see pretty clearly what is going on. Now, under which sorts of circumstances might that be most useful or needed?
I needed LSB this for the following scenario:
Imagine you're building a "mail processor" daemon that downloads the message from an email server, classifies it, parses it, saves it, and then does something, depending on the type of the message.
Class hierarchy: you have a base Message class, with children "BouncedMessage" and "AcceptedMessage".
Each of the message types has its own way to persist itself on disk. For example, all messages of type BouncedMessage try to save itself as BouncedMessage-id.xml. AcceptedMessage, on the other hand, needs to save itself differently - as AcceptedMessage-timestamp.xml. The important thing here is that the logic for determining the filename pattern is different for different subclasses, but shared for all items within the subclass. That's why it makes sense for it to be in a static method.
Base Message class has an abstract static method (yes, abstract AND static) "save". BouncedMessage implements this method with a concrete static method. Then, inside the class that actually retrieves the message, you can call "::save()"
If you want to learn more about the subject:
http://www.qcodo.com/forums/topic.php/2356
http://community.livejournal.com/php/585907.html
http://bugs.php.net/bug.php?id=42681
One primary need I have for late static binding is for a set of static instance-creation methods.
This DateAndTime class is part of a chronology library that I ported to PHP from Smalltalk/Squeak. Using static instance-creation methods enables creation of instances with a variety of argument types, while keeping parameter checking in the static method so that the consumer of the library is unable to obtain an instance that is not fully valid.
Late static binding is useful in this case so that the implementations of these static instance-creation methods can determine what class was originally targeted by the call. Here is an example of usage:
With LSB:
class DateAndTime {
public static function now() {
$class = static::myClass();
$obj = new $class;
$obj->setSeconds(time());
return $obj;
}
public static function yesterday() {
$class = static::myClass();
$obj = new $class;
$obj->setSeconds(time() - 86400);
return $obj;
}
protected static function myClass () {
return 'DateAndTime';
}
}
class Timestamp extends DateAndTime {
protected static function myClass () {
return 'Timestamp';
}
}
// Usage:
$date = DateAndTime::now();
$timestamp = Timestamp::now();
$date2 = DateAndTime::yesterday();
$timestamp2 = Timestamp::yesterday();
Without late static binding, [as in my current implementation] each class must implement every instance creation method as in this example:
Without LSB:
class DateAndTime {
public static function now($class = 'DateAndTime') {
$obj = new $class;
$obj->setSeconds(time());
return $obj;
}
public static function yesterday($class = 'DateAndTime') {
$obj = new $class;
$obj->setSeconds(time() - 86400);
return $obj;
}
}
class Timestamp extends DateAndTime {
public static function now($class = 'Timestamp') {
return self::now($class);
}
public static function yesterday($class = 'Timestamp') {
return self::yesterday($class);
}
}
As the number of instance-creation methods and class-hierarchy increases the duplication of methods becomes a real pain in the butt. LSB reduces this duplication and allows for much cleaner and more straight-forward implementations.
It's useful when:
You have functionality that varies over the class hierarchy,
The functionality has the same signature over the hierarchy, and
(crucially) You don't have an instance to hang the functionality off of.
If only #1 and #2 obtained, you would use an ordinary instance method. So Alex's problem (see his answer to this question) does not require LSB.
A typical case is object creation, where subclasses create themselves in different ways, but using the same parameters. Obviously you have no instance to call, so the creation method (also known as a factory method) must be static. Yet you want its behavior to vary depending on the subclass, so an ordinary static method is not right. See Adam Franco's answer for an example.
If you need to access an overloaded static property/Method within a method that hasn't been overloaded in a subclass - you need late static binding. A quick example: paste2.org
The classic example is the ActiveRecord class from Rails, if you try to implement something similar in PHP, which would look like this: class User extends ActiveRecord and then try to call User::find(1) the method that gets called is actually ActiveRecord::find() because you haven't overloaded find() in User - but without late static binding the find() method in ActiveRecord has no way of knowing which classed it got called from (self within it will always point to ActiveRecord), and thus it can't fetch your User-object for you.
Suppose you have classes representing tables (row instances) in a simplified object-relational mapper.
You would have a class "User" and a class "Company" who's instances are representing rows of the respective tables.
User and Company would inherit from some base abstract class, let's say "BaseObject" that will have some common methods like save(), delete(), validate() etc ...
If you want to store data about the validation and the table definition, the best place would be in a static variable in each derived class - since the validation and table definition is the same for each instance of User.
Without LSB the mentioned validate() method in BaseObject would have no reference to the static variables defined in User and Company, even though you are calling it through an instance of User. It will look for the same static variable in the BaseObject class, and it will raise an error.
This is my experience with PHP 5.2.8 - LSB is going to be introduced in 5.3
I have a class with a static method that handles some formatting. I have another class that than needs all the functionality of the original one except for how it handles formatting.