I know we can implement PHP's countable interface to determine how the function count() works as well as the Iterator interface for using an object like an array.
Is it possible to implement some kind of interface (or any other way) to change the behavior of empty() on an object?
Essentially, this is what I would like to be able to do:
<?php
class test {
function __empty() {
if (count($this->data) > 0) {
return false;
}
return true;
}
}
Just a nice to have!
No there is not. The iterator behavior and count behaviors have no sense over an object; that's why you can override them. The default behavior of empty() applied to an object has a meaning instead: is it an instance of an object or is it NULL?; that's why you can't override it.
You can instead:
create a method inside the object called isEmpty() and implement it there
create a global function emptyObj() and implement it there
No. PHP does not have it. You can request for such feature on wiki.php.net. In the meantime you can roll your own.
interface Empty_Checkable{
function isempty();
}
class test implements Empty_Checkable{
function isempty() {
if (count($this->data) > 0) {
return false;
}
return true;
}
}
This is not possible on the object directly.
If you need to do it, I would suggest implementing the Countable interface, and then instead of calling empty($foo), call count($foo) == 0...
However, for properties, it is possible using a combination of two magic methods: __isset and __get. For example:
class Foo {
protected $bar = false;
protected $baz = true;
public function __get($key) {
return $this->$key;
}
public function __isset($key) {
return isset($this->$key);
}
}
That results in:
empty($foo->abc); // true, isset returns false
empty($foo->bar); // true, isset returns true, but get returns false value
empty($foo->baz); // false, isset returns true, and get returns a true value
So no, it's not possible with a single method handler, but with the combination of the two magic methods, it works fine...
It is possible to implement Countable
class MyCollection extends stdClass implements Serializable, ArrayAccess, Countable, Iterator, JsonSerializable
{
public function count() {
return count($this->array);
}
}
and use !count() instead of empty() like
if (!count($myList)) {
echo "List is empty!";
}
Related
I have a class with methods that I want to use as callbacks.
How can I pass them as arguments?
Class MyClass {
public function myMethod() {
// How should these be called?
$this->processSomething(this->myCallback);
$this->processSomething(self::myStaticCallback);
}
private function processSomething(callable $callback) {
// Process something...
$callback();
}
private function myCallback() {
// Do something...
}
private static function myStaticCallback() {
// Do something...
}
}
Check the callable manual to see all the different ways to pass a function as a callback. I copied that manual here and added some examples of each approach based on your scenario.
Callable
A PHP function is passed by its name as a string. Any built-in or user-defined function can be used, except language constructs such as: array(), echo, empty(), eval(), exit(), isset(), list(), print or unset().
// Not applicable in your scenario
$this->processSomething('some_global_php_function');
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
// Only from inside the same class
$this->processSomething([$this, 'myCallback']);
$this->processSomething([$this, 'myStaticCallback']);
// From either inside or outside the same class
$myObject->processSomething([new MyClass(), 'myCallback']);
$myObject->processSomething([new MyClass(), 'myStaticCallback']);
Static class methods can also be passed without instantiating an object of that class by passing the class name instead of an object at index 0.
// Only from inside the same class
$this->processSomething([__CLASS__, 'myStaticCallback']);
// From either inside or outside the same class
$myObject->processSomething(['\Namespace\MyClass', 'myStaticCallback']);
$myObject->processSomething(['\Namespace\MyClass::myStaticCallback']); // PHP 5.2.3+
$myObject->processSomething([MyClass::class, 'myStaticCallback']); // PHP 5.5.0+
Apart from common user-defined function, anonymous functions can also be passed to a callback parameter.
// Not applicable in your scenario unless you modify the structure
$this->processSomething(function() {
// process something directly here...
});
As of PHP 8.1, we now have first-class callables. They use the syntax $callable = functionName(...). The three dots are part of the syntax and not an omission.
You can use the new syntax to create callable class methods.
Class MyClass {
public function myMethod() {
// first-class callables
$this->processSomething($this->myCallback(...));
$this->processSomething(self::myStaticCallback(...));
}
private function processSomething(callable $callback) {
// Process something...
$callback();
}
private function myCallback() {
// Do something...
}
private static function myStaticCallback() {
// Do something...
}
}
The three dots are not an omission/placeholder for parameters. They are a special syntax for creating a callable. If the method accepts no parameters, the syntax remains the same.
Since 5.3 there is a more elegant way you can write it, I'm still trying to find out if it can be reduced more
$this->processSomething(function() {
$this->myCallback();
});
You can also to use call_user_func() to specify a callback:
public function myMethod() {
call_user_func(array($this, 'myCallback'));
}
private function myCallback() {
// do something...
}
You can set the method return type to callable. It works for PHP 7.1
protected function myMethod(): callable
{
return function (int $j) {
};
}
Then call it like this:
someFunction($this->myMethod());
This question already has answers here:
how to create a php class which can be casted to boolean (be truthy or falsy)
(7 answers)
Closed 8 years ago.
Is there a way to define the boolean returned value for an if statement issued on my class instances?
I have a class implementing the array access interface and so on, but even if the wrapped array is empty:
if($class)
will always return true, since it is an object and does exists.
I'd rather not have to issue everytime an:
if(count($class))
Is there any way to achieve this?
If I get the point you can implement the countable interface; This requires you define the count() method, which let you know (by using it like count($myArrayAccessInstance)) if your internal array has items or not o whatever logic you want to define.
class MyAy implements ArrayAccess, Countable
{
private $data;
public function offsetExists($key)
{
# code...
}
public function offsetUnset($key)
{
# code...
}
public function offsetGet($key)
{
# code...
}
public function offsetSet($key, $value)
{
$this->data[$key] = $key;
}
public function count()
{
return $this->data > 0;
}
}
$my = new MyAy();
$my['user'] = "Ilpaijin";
if(count($my))
{
var_dump($my);
}
Given that you are implementing a class, maybe you could have a member which counts the elements in the array? Something like:
class MyClass {
public $numElems = 0;
private $elems = array();
...
public function add($elem) {
$elems[] = $elem;
$numElems++;
}
...
}
And then, do the iflike if($class->numElems) ...
Its not possible
In PHP an object when cast to bool always produces true. There is no way of changing that.
Check answers here
It's impossible to cast an object to a boolean related to a property inside the class because it always will return true. but I think you just need to create a function inside your class that allows you to return the boolean you need like this
public function bool()
{
return (count($this->array)>0);
}
when you need to use it, just call the function like this if ($obj->bool()) { ... } and another way to make your statements look like what you wanted to do in the begining is to define a class with a really short name, I usually use _, and this function should return the boolean you need just like this
function _ ($Obj)
{
return $Obj->bool();
}
finally, instead of testing on your class like this if ($class->bool()) which is a bit long, you do it like this
if (_($class))
{ /* do something fun */ }
I am curious in writing a chaining interface in PHP OOP. I modified this sample code from the php.net website, and I want to take it further - how can I return objects or arrays from this kind of interface?
// Declare a simple class
class TestClass
{
public $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
public function __toString()
{
return $this->foo;
}
}
$input = (object)array("title" => "page 1");
$class = new TestClass($input);
echo $class;
error,
Catchable fatal error: Method TestClass::__toString() must return a
string value in C:\wamp\www\test\2013\php\fluent_interface.php on line
2
Should I use different magic method instead of __toString then?
EDIT:
Can I return this as my result,
stdClass Object ( [title] => page 1 )
To get what you want you need to use following syntax:
print_r($class->foo);
The __toString() magic method tries to convert your whole class 'TestClass' to a string, but since the magic method is not returning a string, it is showing you that error. Of course you could also rewrite your __toString() method to do the following:
public function __toString()
{
return print_r($this->foo, true);
}
http://php.net/manual/en/function.print-r.php
http://www.php.net/manual/en/language.oop5.magic.php#object.tostring
I think you are looking for either print_r or var_export functions:
public function __toString()
{
return var_export($this->foo, true);
}
and var_export is better since it also return type of value (and, besides, in valid PHP-code format). Note, that __toString() method have nothing common with fluent interface. It's just different things.
I know you can do this
class Object
{
private $ar;
public function __isset($name)
{
return isset($this->ar[$name]);
}
}
which can then be used to do the following
$obj = new Object();
if (isset($obj->name)) { /* ... */ }
However is there a way to do this
$obj = new Object();
if (isset($obj)) { /* .... */ }
Where i can control the return of $obj status using the __isset() magic method on the object it self.
You could only define a new global function myIsset() or something like it to do this.
function myIsset($obj = NULL)
{
...
}
When checking the variable $obj with isset PHP doesn't interact with the object that might be referenced by the variable at all.
You cannot, because it would not make any sense (at least not in the way isset() is meant to be used). So isset($obj) is always true as long as it points to some object and not NULL/undefined.
Magic method __isset is not intended to be used that way.
According to PHP manual:
"__isset() is triggered by calling isset() or empty() on inaccessible properties."
I want to use magic function __set() and __get() for storing SQL data inside a php5 class and I get some strange issue using them inside a function:
Works:
if (!isset($this->sPrimaryKey) || !isset($this->sTable))
return false;
$id = $this->{$this->sPrimaryKey};
if (empty($id))
return false;
echo 'yaay!';
Does not work:
if (!isset($this->sPrimaryKey) || !isset($this->sTable))
return false;
if (empty($this->{$this->sPrimaryKey}))
return false;
echo 'yaay!';
would this be a php bug?
empty() first* calls the __isset() method and only if it returns true the __get() method. i.e. your class has to implement __isset() as well.
E.g.
<?php
class Foo {
public function __isset($name) {
echo "Foo:__isset($name) invoked\n";
return 'bar'===$name;
}
public function __get($name) {
echo "Foo:__get($name) invoked\n";
return 'lalala';
}
}
$foo = new Foo;
var_dump(empty($foo->dummy));
var_dump(empty($foo->bar));
prints
Foo:__isset(dummy) invoked
bool(true)
Foo:__isset(bar) invoked
Foo:__get(bar) invoked
bool(false)
* edit: if it can't "directly" find an accessible property in the object's property hashtable.