views:

170

answers:

4

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.

+1  A: 

Please look at method chaining.

More information

Marien
Please look at the Moon. Can you give him a clearer answer instead? :)
Jonathan Sampson
Indeed, that's not relevant to this question.
Alix Axel
I'm sorry, i missed something and thought this was the solution :)
Marien
@eyze: it is very relevant and you should read it. But Marien give no advice on how to apply what you learn there to your problem.
OIS
@OIS: I'm familiar with method chaining but it doesn't offer a solution to my question.
Alix Axel
+5  A: 

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();
Tom Haigh
+1, That was what I though, I was also considering returning the object domain and if no method of the domain was called then the __toString would come into action and return true, otherwise is the string was not a domain it would simply return false. Do you think it's a good ideia?
Alix Axel
Please take a look at my edit.
Alix Axel
@eyze: No. I just tested and objects when cast to bools are always true, __toString() is not called
Tom Haigh
@eyze: But then Is::Domain() will return a bool, and you won't be able to chain. To chain you need to return the object that the next method will be called on.
Tom Haigh
You're absolutely right. Oh well, it isn't possible then. =|
Alix Axel
+3  A: 

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();
OIS
Please notice that I'm using the __get magic method.
Alix Axel
@eyze: that is irrelevant to the question. You can add what you need there yourself. The class is just to demonstrate chaining, and poorly at that. The task would probably be better with adding the domain as param to the constructor.
OIS
+1, That is a valid solution however, not the one I'm looking for.
Alix Axel
A: 

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.

Zed
The only problem is, how would I return true instead of a object if the Domain::Available() method isn't called afterwards?
Alix Axel