It seems like PHP allows having strings (like the string foo in the example below) before parameters in function definitions.
function do_something(foo $param){}
Is this some kind of a feature?
This is some kind of type-safety feature in PHP
If you have
class Something {
public function add(Something $s) { .. }
}
$s = new Something();
$s->add(new stdCLass());
This will throw catchable fatal error.
You can see it here - http://php.net/manual/en/language.oop5.typehinting.php
Yes, it's type hinting.
Yeah, In your example you are using type hinting, saying that parameter $param must be an instance of class "foo"
It is a way to define what type is allowed to be passed to the function. If you look at this comment on the PHP.net Function Arguments page you will see that you can define a class to be the type that is allowed.
Related
I've been working on code that's intended to be used with objects, without really caring what the kind of object is. I wanted to type hint that the method being written expected an object of any type, but ran into some difficulty.
I tried function myFunc (object $obj) and function myFunc (stdClass $obj) but both of these generated errors when I tried to pass objects in:
Catchable fatal error: Argument 1 passed to MyClass::MyFunc() must be an instance of object, instance of ObjectActualClass given
The same happened with stdClass as well
What am I missing? I thought that all classes that didn't explicitly inherit from another class inherited from stdClass, meaning that the base class of every class in PHP would be stdClass. Is this not the case?
stdClass is NOT a base class! PHP classes do not automatically inherit from any class. All classes are standalone, unless they explicitly extend another class. PHP differs from many object-oriented languages in this respect.
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.
Well it only took eight years, but this will soon be possible: PHP 7.2 introduces the object type hint! As I write this, it's currently in the RFC stage, and is due to be released in November.
Update, 30th November: PHP 7.2 has been released
RFC: Object typehint
Discussion
This behaves exactly as you might expect:
<?php
class Foo {}
class Bar {}
function takeObject(object $obj) {
var_dump(get_class($obj));
}
takeObject(new Foo);
takeObject(new Bar);
takeObject('not an object');
Will result in:
string(3) "Foo"
string(3) "Bar"
Fatal error: Uncaught TypeError: Argument 1 passed to takeObject() must be an object, string given, called in...
See https://3v4l.org/Svuij
One side-effect of this is that object is now a reserved word, which unfortunately renders #Gaz_Edge's existing solution above broken. Fortunately, all you have to do to fix it is delete the interface.
Although there is no type hinting for objects, you can use:
if (!is_object($arg)) {
return;
}
There is no base class that all objects extend from. You should just remove the typehint and document the expected type in the #param annotation.
There is no built-in mechanism to do this without requiring all users of your interface to extend a specified class. But why would you want to do this anyway? What do all object types have in common that's enough to make them suitable input for your API?
In all probability you wouldn't gain anything even if able to type hint like this. On the other hand, type hinting a parameter to implement an interface (such as Traversable) would be much more meaningful.
If you still want something akin to type hinting, the best you can do is substitute a runtime check with is_object on the parameter.
As of php 7.2 this feature has now been implemented. you can type hint for any object now.
function myFunc(object $myObject): object {
return $myObject;
}
You can review this in the official documentation
Typehint for stdClass works since PHP 5.3+ (if I am not wrong).
Following is valid code using typehint for stdClass construct:
Example test.php:
class Test{
function hello(stdClass $o){
echo $o->name;
}
}
class Arg2 extends stdClass{
public $name = 'John';
function sayHello(){
echo 'Hello world!';
}
}
$Arg1 = new stdClass();
$Arg1->name = 'Peter';
$Arg2 = new Arg2();
$Arg2->sayHello();
$test = new Test();
// OK
$test->hello($Arg1);
$test->hello($Arg2);
// fails
$test->hello(1);
Prints out:
Hello world!
Peter
John
Catchable fatal error: Argument 1 passed to Test::hello() must be an instance of stdClass, integer given, called in test.php on line 32 and defined in test.php on line 5
You could do something like this:
function myFunc ($obj)
{
if ($obj instanceof stdClass) { .... }
}
I'm having some troubles with some object oriented programming in PHP.
I basically have a function within a class, which displays some text:
public function text() {
echo 'text';
}
Now I want to store the function name in an arra,y so I can reference it later:
$functions = array('text_func' => 'text');
And in an other function I want to call the above function with the Array reference, so I did the following:
public function display() {
$this->functions['text_func']();
}
The result will be basically:
text();
My question is that, how is it possible to make this function run? (The correct way it should be look like: $this->text()). However I can't do something like this, because it gives me an error:
$this->$this->functions['text_func']();
Any idea or solution for my problem?
The error message you carefully ignore probably warns you that $this cannot be converted to string. That gives you a pretty good clue of what's going on.
(Long answer is that method names are strings. Since PHP is loosely typed, when it needs a string it'll try to convert whatever you feed it with into string. Your class lacks a __toString() method, thus the error.)
You probably want this:
$this->{$this->functions['text_func']}();
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here's an example from PHP.net that should help:
<?php
class Foo
{
function Variable()
{
$name = 'Bar';
$this->$name(); // This calls the Bar() method
}
function Bar()
{
echo "This is Bar";
}
}
$foo = new Foo();
$funcname = "Variable";
$foo->$funcname(); // This calls $foo->Variable()
?>
Source: http://www.php.net/manual/en/functions.variable-functions.php
You're confusing a lot of things. First of all, you can use the magic constants __METHOD__ and __CLASS__ to determine in which class type and method you are currently executing code.
Secondly, a callable in PHP is defined as either an array with an object instance or static class reference at index 0, and the method name at index 1, or since PHP 5.2 a fully qualified string such as MyClass::myFunction. You can only dynamically invoke functions stored as one of these types. You can use the type hint callable when passing them between functions.
$this does not contain a reference for itself, so you should try just
$this->display();
instead of
$this->$this->functions['text_func'](); // WRONG
All about a Zend Application with an action helper.
I want to unset some pairs of an array by a function.
helper:
class Application_Controller_Action_Helper_TestHelper extends Zend_Contr[...]
{
public function direct(&$array)
{
if(isset($array['key']))
unset($array['key']);
}
}
controller:
$this->_helper->TestHelper($var);
How could I get it working?
Since you are now passing by reference, you can modify the variable in the method and the changes will be applied to the original variable. However, the way you have it now you are not changing the variable at all, just returning the result of the expression, like in your first example. You should have something like this instead:
class Application_Controller_Action_Helper_TestHelper extends Zend_Contr[...] {
public function direct(&$var) {
$var = $var + 1;
}
}
You must also pass it as reference:
$this->_helper->TestHelper(&$var);
UPDATE:
Ups, I had my errors turned off. You (and now me) are getting the error because...
There is no reference sign on a function call - only on function
definitions. Function definitions alone are enough to correctly pass
the argument by reference. As of PHP 5.3.0, you will get a warning
saying that "call-time pass-by-reference" is deprecated when you use &
in foo(&$a);.
ZF's HelperBroker uses return call_user_func_array(array($helper, 'direct'), $args); to call your direct() method. Check the docs, but it seems call_user_func_array passes by reference, although with several quirks.
Check out this answer.
I've been working on code that's intended to be used with objects, without really caring what the kind of object is. I wanted to type hint that the method being written expected an object of any type, but ran into some difficulty.
I tried function myFunc (object $obj) and function myFunc (stdClass $obj) but both of these generated errors when I tried to pass objects in:
Catchable fatal error: Argument 1 passed to MyClass::MyFunc() must be an instance of object, instance of ObjectActualClass given
The same happened with stdClass as well
What am I missing? I thought that all classes that didn't explicitly inherit from another class inherited from stdClass, meaning that the base class of every class in PHP would be stdClass. Is this not the case?
stdClass is NOT a base class! PHP classes do not automatically inherit from any class. All classes are standalone, unless they explicitly extend another class. PHP differs from many object-oriented languages in this respect.
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.
Well it only took eight years, but this will soon be possible: PHP 7.2 introduces the object type hint! As I write this, it's currently in the RFC stage, and is due to be released in November.
Update, 30th November: PHP 7.2 has been released
RFC: Object typehint
Discussion
This behaves exactly as you might expect:
<?php
class Foo {}
class Bar {}
function takeObject(object $obj) {
var_dump(get_class($obj));
}
takeObject(new Foo);
takeObject(new Bar);
takeObject('not an object');
Will result in:
string(3) "Foo"
string(3) "Bar"
Fatal error: Uncaught TypeError: Argument 1 passed to takeObject() must be an object, string given, called in...
See https://3v4l.org/Svuij
One side-effect of this is that object is now a reserved word, which unfortunately renders #Gaz_Edge's existing solution above broken. Fortunately, all you have to do to fix it is delete the interface.
Although there is no type hinting for objects, you can use:
if (!is_object($arg)) {
return;
}
There is no base class that all objects extend from. You should just remove the typehint and document the expected type in the #param annotation.
There is no built-in mechanism to do this without requiring all users of your interface to extend a specified class. But why would you want to do this anyway? What do all object types have in common that's enough to make them suitable input for your API?
In all probability you wouldn't gain anything even if able to type hint like this. On the other hand, type hinting a parameter to implement an interface (such as Traversable) would be much more meaningful.
If you still want something akin to type hinting, the best you can do is substitute a runtime check with is_object on the parameter.
As of php 7.2 this feature has now been implemented. you can type hint for any object now.
function myFunc(object $myObject): object {
return $myObject;
}
You can review this in the official documentation
Typehint for stdClass works since PHP 5.3+ (if I am not wrong).
Following is valid code using typehint for stdClass construct:
Example test.php:
class Test{
function hello(stdClass $o){
echo $o->name;
}
}
class Arg2 extends stdClass{
public $name = 'John';
function sayHello(){
echo 'Hello world!';
}
}
$Arg1 = new stdClass();
$Arg1->name = 'Peter';
$Arg2 = new Arg2();
$Arg2->sayHello();
$test = new Test();
// OK
$test->hello($Arg1);
$test->hello($Arg2);
// fails
$test->hello(1);
Prints out:
Hello world!
Peter
John
Catchable fatal error: Argument 1 passed to Test::hello() must be an instance of stdClass, integer given, called in test.php on line 32 and defined in test.php on line 5
You could do something like this:
function myFunc ($obj)
{
if ($obj instanceof stdClass) { .... }
}
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