PHP object return false if no properties - php

I am trying to make a class what will contain a set of properties. it will be used like so:
$class = new test_class();
$class->property;
$class->second_property;
Basically, if the properties exist, then they are true, if the properties do not exist, they are false. The properties have no value, only existence.
Now, I want to do something like this:
$class = new test_class();
var_dump($class->property); // false - property does not exist
var_dump($class->second_property); // true - second_property exists
var_dump( (bool) $class); // true
So if even one property of the test class exists, var dumping the $class will show true because it is an object.
However, in the situation where the class has no properties, I want this to happen:
$class = new test_class();
var_dump($class->property); // false - property does not exist
var_dump($class->second_property); // false - second_property does not exist
var_dump( (bool) $class); // false
But, I still want $class to be instanceof the test_class but return false in a logic test.
Is this at all possible? If so, how would I do it?
Thanks, Ozzy
edit:
To clarify, I am already using the __get() magic function. What I want to happen is if the test_class has no properties, then when a var_dump is performed on it, it returns false but an instanceof will return test_class.
elaborating...
I am creating a complex permissions system.
A user gets assigned sections and each section has a set of permissions.
It will work like this:
$user = permissions::get_user(USER_ID_HERE);
// Every property of the $user is a section
var_dump($user->this_section_exists); // true - returns an object
var_dump($user->this_section_doesnt); // false - returns a boolean
If a section exists, then it returns an object of the sections permissions.
var_dump($user->this_section_exists); // true
var_dump($user->this_section_exists->this_permission_exists); // true
var_dump($user->this_section_exists->this_permission_doesnt); // false
Heres the edge case:
var_dump($user->this_section_doesnt); // false
var_dump($user->this_section_doesnt->some_permission);
// This should also return false, which it does,
// But it throws a notice: "Trying to get property of non-object"
I want to be able to either suppress that notice without any modifications to the code which calls the class, ie, no # to suppress.. or be able to return an object with no properties which evaluates to false on a logic test.

What I want to happen is if the test_class has no properties, then when a var_dump is performed on it, it returns false but an instanceof will return test_class.
That is not possible, var_dump will always show you the correct type, that's what it was made for.

No, you can't do it exactly as you wrote it. But you can emulate something similar:
class A {
public function __toString() { // this is what you want, but it only works when you use your object as a string, not as a bool.
if (count(get_class_vars(__CLASS__))) { // so if the class has at least 1 attribute, it will return 1.
return '1';
} else {
return '0';
}
}
}
$a = new A;
var_dump((bool)(string)$a); // false
If I add a propery in A class it will return true. You can also use it without (bool).
if ((string)$a) {
echo 'I am not empty';
} else {
echo 'I am an empty object';
}
If you don't want to use (string), you have one option left, to create a method that contains the code from __toString() and call it instead of casting.
References:
__toString()
get_class_vars()
__CLASS__
PS: About what you said - doing a var_dump() on the object to return false. No, that is not possible.

You can do some wrapping with __get() to manipulate the answer.
Say something like
class test_class {
private $_validProperties;
public function __construct()
{
$this->validProperties = array('foo', 'bar', 'widget');
}
public function __get($prop)
{
if (isset($this->_validProperties[$prop])
{
return true;
}
return false;
}
}
$a = new test_class();
var_dump($a->foo); //true
var_dump($a->tree); //false

You can try PHP magic function
<?php
class Test {
private $name = 'name';
public $age = 20;
public function __get($name) {
return isset($this->$name) ? true : false;
}
public function __toString() {
return true;
}
}
$object = new Test();
var_dump($object->name); // output `true`
var_dump($object->age); // output `20`
var_dump($object->notfound); // output `false`
var_dump((bool)$object); // output `true`

If I understand you correctly, I can only do so.
Hope it can help you
<?php
class Permissions {
private $userPermissions = array(
1 => array(
'Blog' => array('Post'),
),
);
private $permission;
public function __construct($id) {
$this->permission = $this->userPermissions[$id];
}
public function __get($name) {
if(isset($this->permission[$name])) {
return new $name($this->permission[$name]);
}
return new Notfound();
}
}
class Blog {
private $permission;
public function __construct($permission) {
$this->permission = $permission;
}
public function __get($name) {
if(($key = array_search($name, $this->permission)) !== false) {
return new $name();
}
return new Notfound();
}
public function __tostring() {
return true;
}
}
class Post {
public function __get($name) {
return isset($this->$name) ? true : new Notfound();
}
public function __tostring() {
return true;
}
}
class Notfound {
public function __get($name) {
return false;
}
public function __tostring() {
return false;
}
}
$user = new Permissions(1);
var_dump('Blog ', $user->Blog); // return Blog
var_dump('Blog:Post', $user->Blog->Post); // return Post
var_dump('News', $user->News); // return Notfound
var_dump('News:Post', $user->News->Post); // return false

Related

PHP - Check if a class member is explicitly set or not

If I check for isset($var), I won't be able to differentiate between following 2 cases. In the first one, I am explicitly setting $t1->a to null whereas in the second, it's left unchanged.
<?php
class Test {
public $a;
}
$t1 = new Test();
$t1->a = null;
if(isExplicitlySet($t1->a)) {
echo "yes t1\n";
}
$t2 = new Test();
if(isExplicitlySet($t2->a)) {
echo "yes t2\n";
}
function isExplicitlySet($var) {
//what goes here?
}
Edit : Reason I need this feature is : Before persisting an object of class Test to Database, I need to know if $a was explicitly set to null or was left unchanged. In the later case, I would set it to its default DB value as specified in the table definition.
Ok, since you are writing your own ORM, it might make sense to use the magic methods (as suggested by Machavity). You could create a parent class
abstract class DB_Obj {
protected $attributes = array();
protected $attributes_have_been_set = array();
public function __set($name, $value) {
$this->attributes[$name] = $value;
$this->attributes_have_been_set[$name] = TRUE;
}
public function __get($name) {
return $this->attributes[$name];
}
public function __isset($name) {
return array_key_exists($name, $this->attributes_have_been_set);
}
}
and extend it
class Test extends DB_Obj {
protected $attributes = array(
'a' => NULL
);
}
When you test it now like this, it works properly
$t1 = new Test();
$t1->a = null;
$t2 = new Test();
var_dump( isset($t1->a), isset($t2->a) );
// bool(true) bool(false)
The advantage of this is also, when you want to save it to the db, you don't need to know each attribute's name (or use another function), but can just iterate over the $attributes array.
You can see an answer here Check if value isset and null
By using get_defined_vars
$foo = NULL;
$vars = get_defined_vars();
if (array_key_exists('bar', $vars)) {}; // Should evaluate to FALSE
if (array_key_exists('foo', $vars)) {}; // Should evaluate to TRUE
I would personnally create a class called UntouchedProperty and set my properties to it at instanciation. Then, untouched and set to null will be different.
class UntouchedProperty {}
class Foo
{
public $bar;
public function __construct()
{
$this->bar = new UntouchedProperty;
}
public function wasTouched($property)
{
if ($this->$property instanceof 'UntouchedProperty') {
return false;
}
return true;
}
}
$foo = new Foo;
$foo->wasTouched('bar'); #=> false
$foo->bar = null;
$foo->wasTouched('bar'); #=> true

PHP Chaining methods

class AAA
{
function getRealValue($var)
{
$this->var = $var;
return $this;
}
function asString()
{
return (string) $this->var;
}
}
$a = new AAA;
$a->getRealValue(30);
$a->getRealValue(30)->asString();
So when i call $a->getRealValue(30) it should return 30,
but when i call $a->getRealValue(30)->asString() it should return '30' as string'.
Thank you
So when i call $a->getRealValue(30) it should return 30,but when i call $a->getRealValue(30)->asString() it should return '30' as string'.
This is not possible (yet). When getRealValue returns a scalar value, you cannot call methods on it.
Apart from that, your class makes little sense to me. Your method is called getRealValue but it accepts an argument and sets the value. So it should be called setRealValue. Method chaining aside, could it be are you looking for a ValueObject?
class Numeric
{
private $value;
public function __construct($numericValue)
{
if (false === is_numeric($numericValue)) {
throw new InvalidArgumentException('Value must be numeric');
}
$this->value = $numericValue;
}
public function getValue()
{
return $this->value;
}
public function __toString()
{
return (string) $this->getValue();
}
}
$fortyTwo = new Numeric(42);
$integer = $fortyTwo->getValue(); // 42
echo $fortyTwo; // "42"
Its not true, $a->getRealValue(30) will return object $a not value. But asString will return value in string format.
Usually when you want to get something like this you do:
$a->getRealValue(30)->get();
//Or
$a->getRealValue(30)->getAsString();

How to return false when chaining methods

I have a validation class which uses method chaining. I would like to be able to do single checks with TRUE/FALSE like this:
if ($obj->checkSomething()) {}
But also chain methods like this:
if ($obj->checkSomething()->checkSomethingElse()) {}
The problem however is that if one method returns FALSE, it will not send back an object and thus breaks the method chaining which ends with this error:
Fatal error: Call to a member function checkSomething() on a non-object in ...
Do I have to pick either single method return calls or method chaining or is there a workaround?
One idea would be to set an internal flag to indicate success or failure, and access it via another method, while checking that flag in each method and not doing anything if it's set. E.g.:
class A {
private $valid = true;
public function check1() {
if (!$this->valid) {
return $this;
}
if (!/* do actual checking here */) {
$this->valid = false;
}
return $this;
}
public function check2() {
if (!$this->valid) {
return $this;
}
if (!/* do actual checking here */) {
$this->valid = false;
}
return $this;
}
public function isValid() {
return $this->valid;
}
}
// usage:
$a = new A();
if (!$a->check1()->check2()->isValid()) {
echo "error";
}
To minimize the boilerplate checking in each function, you could also use the magic method __call(). E.g.:
class A {
private $valid;
public function __call($name, $args) {
if ($this->valid) {
$this->valid = call_user_func_array("do" . $name, $args);
}
return $this;
}
private function docheck1() {
return /* do actual checking here, return true or false */;
}
private function docheck2() {
return /* do actual checking here, return true or false */;
}
public isValid() {
return $this->valid;
}
}
The usage would be same as above:
$a = new A();
if (!$a->check1()->check2()->isValid()) {
echo "error";
}
I believe you're looking to have an instance evaluate as true/false based on the outcome of validation.
While some languages allow you to override the boolean value of an instance, php does not (except for casting to string, that is. See PHP's Magic Methods).
Also, the booleans page in the PHP Manual has a list of things that evaluate to false, but it doesn't give any method of overriding the behavior either.
That being said, I'd suggest going with JRL's idea, and construct a chain of validation rules, then 'execute' it with a function that returns the boolean needed in your if statement.
You could wrap them up in a subclass, perhaps.
e.g. if you have
class Validate {
public function checkSomething($data) {
if ($data === $valid) {
return true;
}
return false;
}
public function checkSomethingElse($data) {
if ($data === $valid) {
return true;
}
return false;
}
}
You could do this:
class ValidateChain extends Validate {
protected $valid = true;
public function checkSomething($data) {
if (false === parent::checkSomething($data)) {
$this->valid = false;
}
return $this;
}
public function checkSomethingElse($data) {
if (false === parent::checkSomethingElse($data)) {
$this->valid = false;
}
return $this;
}
public function getIsValid() {
return $this->valid;
}
}
$v = new ValidationChain();
$valid = $v->checkSomething()->checkSomethingElse()->getIsValid();
Quick and dirty, E&OE. And you'd probably need to add a way to find out which bits weren't valid, etc.

Is this possible in PHP?

Consider the following PHP snippet:
<?php
class Is
{
function __get($key)
{
$class = __CLASS__ . '_' . $key;
if (class_exists($class) === true)
{
return $this->$key = new $class();
}
return false;
}
function Domain($string)
{
if (preg_match('~^[0-9a-z\-]{1,63}\.[a-z]{2,6}$~i', $string) > 0)
{
return true;
}
return false;
}
}
class Is_Domain
{
function Available($domain)
{
if (gethostbynamel($domain) !== false)
{
return true;
}
return false;
}
}
$Is = new Is();
var_dump($Is->Domain('google.com')); // true
var_dump($Is->Domain->Available('google.com')); // false
?>
Is it possible to call the Available() method like this (and still return solely true or false if the Available method is not called)?
var_dump($Is->Domain('google.com')->Available()); // false
If yes, how?
EDIT: Would this do the trick?
class Is
{
function __get($key)
{
// same as before
}
function Domain($string)
{
if (preg_match('~^[0-9a-z\-]{1,63}\.[a-z]{2,6}$~i', $string) > 0)
{
return (bool) $this->Domain;
}
return false;
}
}
class Is_Domain
{
function __toString()
{
return true;
}
function Available($domain)
{
if (gethostbynamel($domain) !== false)
{
return true;
}
return false;
}
}
Thanks in Advance!
PS: This snippet is truncated, so don't expect it to make it a lot of sense just by its own.
Essentially you want a method to return either a bool or an object based on whether a subsequent method call to the result is going to occur. I don't think this will be possible without some massive hack (e.g. reading the PHP file in yourself and looking ahead), and it shouldn't be because your objects shouldn't be worrying about the context in which they are used.
Instead you could get the first call to return an object which is relevant in both cases, e.g. DomainLookupResult, which has two methods e.g. Exists() and IsAvailable(). You could then do:
$result = $Is->Domain('google.com');
$isValid = $result->Exists();
$isAvaliable = $result->IsAvailable();
//or chaining:
$isValid = $Is->Domain('google.com')->Exists();
$isAvailable = $Is->Domain('google.com')->IsAvailable();
You can only chain method calls if they return an object!
This is because you can only call methods on objects.
The problem with your code is that the methods return a non object value, either true or false. And the problem is not in any way solved better by chaining methods. You should use that where its applicable. Like chaining many setters, NOT getters which the methods you want to use essentially is.
var_dump($Is->Domain->Available('google.com')); // false
//is the same as
$res = $Is->Domain;
$res = $res->Available('google.com'));
var_dump($res);
So you see the first res is a boolean true or false, and you can not call a method on that.
edit
This might be a "solution". Not a good solution though since this is better without chaining.
class Domain
{
public $domain;
function setDomain($domain) {
$this->domain = $domain;
return $this;
}
function isDomain($domain = null) {
if (is_string($domain)) {
$this->setDomain($domain);
}
$result = gethostbynamel($this->domain) !== false;
return new Result($this, $result);
}
function isValid() {
$result = (bool) preg_match('', $this->domain);
return new Result($this, $result)
}
}
class Result
{
public $result;
public $object;
public function __construct($object, $result)
{
$this->result = $result;
$this->object = $object;
}
public function __call($method, $arguments)
{
if (is_object($this->result)) {
return call_user_func_array(array($this->result, $method), $arguments);
}
if (!$this->result) {
return $this;
}
return call_user_func_array(array($this->object, $method), $arguments);
}
}
$domain = new Domain();
var_dump($domain->isValid('google.com')->isAvailable()->result);
/edit
This will solve your problem above.
var_dump($Is->Domain('validandfreedomain.com') && $Is_Domain->Available('validandfreedomain.com')); // true
If you desperately want to chain a method for this problem you could make it more like this.
class Domain
{
public $domain;
function setDomain($domain) {
$this->domain = $domain;
return $this;
}
function isAvailable() {
return gethostbynamel($this->domain) !== false;
}
function isValid() {
return (bool) preg_match('', $this->domain);
}
}
$domain = new Domain();
$result = $domain->setDomain('validandfreedomain.com')->isValid() && $domain->isAvailable();
It is possible, if your function returns an object, you can call its method, and so on (see method chaining). The only limitation is - as far as a I know - is that you cannot chain calls from an object created by new ( new Object()->method1()->method2() ).
As for your example, I see no point in using either the dynamic class, or method chaining stuff.

PHP: How do I check if all public methods of two classes return the same values?

In effect, if I have a class c and instances of $c1 and $c2
which might have different private variable amounts but all their public methods return the same values I would like to be able to check that $c1 == $c2?
Does anyone know an easy way to do this?
You can also implement a equal($other) function like
<?php
class Foo {
public function equals($o) {
return ($o instanceof 'Foo') && $o.firstName()==$this.firstName();
}
}
or use foreach to iterate over the public properties (this behaviour might be overwritten) of one object and compare them to the other object's properties.
<?php
function equalsInSomeWay($a, $b) {
if ( !($b instanceof $a) ) {
return false;
}
foreach($a as $name=>$value) {
if ( !isset($b->$name) || $b->$name!=$value ) {
return false;
}
}
return true;
}
(untested)
or (more or less) the same using the Reflection classes, see http://php.net/manual/en/language.oop5.reflection.php#language.oop5.reflection.reflectionobject
With reflection you might also implement a more duck-typing kind of comparision, if you want to, like "I don't care if it's an instance of or the same class as long as it has the same public methods and they return the 'same' values"
it really depends on how you define "equal".
It's difficult to follow exactly what you're after. Your question seems to imply that these public methods don't require arguments, or that if they did they would be the same arguments.
You could probably get quite far using the inbuilt reflection classes.
Pasted below is a quick test I knocked up to compare the returns of all the public methods of two classes and ensure they were they same. You could easily modify it to ignore non matching public methods (i.e. only check for equality on public methods in class2 which exist in class1). Giving a set of arguments to pass in would be trickier - but could be done with an array of methods names / arguments to call against each class.
Anyway, this may have some bits in it which could be of use to you.
$class1 = new Class1();
$class2 = new Class2();
$class3 = new Class3();
$class4 = new Class4();
$class5 = new Class5();
echo ClassChecker::samePublicMethods($class1,$class2); //should be true
echo ClassChecker::samePublicMethods($class1,$class3); //should be false - different values
echo ClassChecker::samePublicMethods($class1,$class4); //should be false -- class3 contains extra public methods
echo ClassChecker::samePublicMethods($class1,$class5); //should be true -- class5 contains extra private methods
class ClassChecker {
public static function samePublicMethods($class1, $class2) {
$class1methods = array();
$r = new ReflectionClass($class1);
$methods = $r->getMethods();
foreach($methods as $m) {
if ($m->isPublic()) {
#$result = call_user_method($m->getName(), $class1);
$class1methods[$m->getName()] = $result;
}
}
$r = new ReflectionClass($class2);
$methods = $r->getMethods();
foreach($methods as $m) {
//only comparing public methods
if ($m->isPublic()) {
//public method doesn't match method in class1 so return false
if(!isset($class1methods[$m->getName()])) {
return false;
}
//public method of same name doesn't return same value so return false
#$result = call_user_method($m->getName(), $class2);
if ($class1methods[$m->getName()] !== $result) {
return false;
}
}
}
return true;
}
}
class Class1 {
private $b = 'bbb';
public function one() {
return 999;
}
public function two() {
return "bendy";
}
}
class Class2 {
private $a = 'aaa';
public function one() {
return 999;
}
public function two() {
return "bendy";
}
}
class Class3 {
private $c = 'ccc';
public function one() {
return 222;
}
public function two() {
return "bendy";
}
}
class Class4 {
public function one() {
return 999;
}
public function two() {
return "bendy";
}
public function three() {
return true;
}
}
class Class5 {
public function one() {
return 999;
}
public function two() {
return "bendy";
}
private function three() {
return true;
}
}
You can define PHP's __toString magic method inside your class.
For example
class cat {
private $name;
public function __contruct($catname) {
$this->name = $catname;
}
public function __toString() {
return "My name is " . $this->name . "\n";
}
}
$max = new cat('max');
$toby = new cat('toby');
print $max; // echoes 'My name is max'
print $toby; // echoes 'My name is toby'
if($max == $toby) {
echo 'Woohoo!\n';
} else {
echo 'Doh!\n';
}
Then you can use the equality operator to check if both instances are equal or not.
HTH,
Rushi
George: You may have already seen this but it may help: http://usphp.com/manual/en/language.oop5.object-comparison.php
When using the comparison operator (==), object variables are compared in a simple manner, namely: Two object instances are equal if they have the same attributes and values, and are instances of the same class.
They don't get implicitly converted to strings.
If you want todo comparison, you will end up modifying your classes. You can also write some method of your own todo comparison using getters & setters
You can try writing a class of your own to plugin and write methods that do comparison based on what you define. For example:
class Validate {
public function validateName($c1, $c2) {
if($c1->FirstName == "foo" && $c2->LastName == "foo") {
return true;
} else if (// someother condition) {
return // someval;
} else {
return false;
}
}
public function validatePhoneNumber($c1, $c2) {
// some code
}
}
This will probably be the only way where you wont have to modify the pre-existing class code

Categories