...

View Full Version : Classes Accessing Outside Info?



Joseph Witchard
03-01-2010, 08:59 PM
If you set up a class, what all can you access in the class that was created outside the class?

Inigoesdr
03-01-2010, 09:08 PM
Global variables and constants, but if your class is well-designed any dynamic data would usually be passed in via the constructor or setters.

Joseph Witchard
03-01-2010, 09:13 PM
So if I wanted to make a class that processes an email form, I could do:



class goForm
{

public function varProcess()
{

// do something with $_POST array
}

}

if ($_POST)
{

$email = new goForm;
$email->varProcess();

}


?

MattF
03-01-2010, 10:09 PM
$_POST is global, so yes, that would work fine. You'd be better doing:



class goForm
{

public function varProcess($post)
{
// do something with $post array
}

}

if ($_POST)
{

$email = new goForm;
$email->varProcess($_POST);

}


however, and then you aren't limited to just processing $_POST vars.

Fou-Lu
03-01-2010, 11:35 PM
$_POST is global, so yes, that would work fine. You'd be better doing:



class goForm
{

public function varProcess($post)
{
// do something with $post array
}

}

if ($_POST)
{

$email = new goForm;
$email->varProcess($_POST);

}


however, and then you aren't limited to just processing $_POST vars.

Classes are independant of environment, they should be designed to handle any type of data, whether its posted, read from a file, scanned from a database, etc.
This is where polymorphism really shines, lets do a quick and dirty example:


interface IRequest
{
public function getRequest($var);
public function setRequest($var, $val);
}

interface IResponse
{
public function getResponse($var);
public function setResponse($var, $val);
}

// Now we have a class that does stuff:
class MyClass
{
public function doSomething(IRequest $req, IResponse $res)
{
$res->setResponse('result', 'Request was: ' . $req->getRequest('request'));
}
}

// And a wrapper of some sorts:
class POSTWrapper implements IRequest, IResponse
{
private $req;
private $res;

public function __construct()
{
// We'll just go without a pass here
$this->req = $_POST;
$this->res = array();
}

public function getRequest($var)
{
return $this->req[$var];
}
public function setRequest($var, $val)
{
$this->req[$var] = $val;
}
public function getResponse($var)
{
return $this->res[$var];
}
public function setResponse($var, $val)
{
$this->res[$var] = $val;
}
}


Usage:


$wrapper = new POSTWrapper(); // Or factory this up, which would look something like:
// $wrapper = WrapperFactory::createWrapper('POST'); for example
$class = new MyClass();
$class->doSomething($wrapper);

print_r($wrapper);


(BTW, I have no idea if the above actually works; I'll be needing to buy a new home PC soon :'()

Combine the wrapper into a factory and you'll see some real power. Swap out a POSTWrapper for a FILEWrapper instead and you can read/write from files instead of request/response styles through post. Only thing that changes is the $wrapper call, everything else would be good to go (assuming its been programmed of course hah).
Notice I did use the _POST superglobal in the POSTWrapper class, but with this example it suits it; there is no reason for me to pass a superglobal into a class who's sole purpose is to manage that type of data (_POST in this example). Although there is absolutely nothing wrong with it either, the reason why I use this approach is this way the wrapper itself doesn't need to accept a parameter to it, which will reduce the complication of expanding the system to cover things like file handling and whatnots.

Joseph Witchard
03-03-2010, 06:50 AM
This probably sounds silly, but what is an interface used for? I've just begun learning about classes. I had no idea there was such a thing as Interfaces.

Dormilich
03-03-2010, 08:24 AM
(BTW, I have no idea if the above actually works; I'll be needing to buy a new home PC soon :'()

Iíd say, second argument missing in MyClass::doSomething()

Interfaces (simplified): they allow you to specify, that a certain class or object has the methods you want to use later.

class MyClass
{
# if $req has no (public) getRequest() and setRequest() method, an error will be thrown
# if $res has no (public) getResponse() and setResponse() method, an error will be thrown
public function doSomething(IRequest $req, IResponse $res)
{
$res->setResponse('result', 'Request was: ' . $req->getRequest('request'));
}
}

# this class must at least have/define the methods specified in the Interfaces
class POSTWrapper implements IRequest, IResponse

Fou-Lu
03-03-2010, 03:29 PM
Iíd say, second argument missing in MyClass::doSomething()


Hah, yes indeed, the above example of mine should have been:


$class->doSomething($wrapper, $wrapper);


I'm a strong believer that the two values should exist, one for the request and one for the response. The post wrapper itself does handle both, but should be considered independant for expansion purposes.

Interfaces are a contract, they are supposed to guarentee that methods exist within a class. So with this signature on a class method:


public function doSomething(IRequest $req, IResponse $res)

I'm supposed to be guarenteed that $req has an publically accessable method called getRequest.

However, PHP fails when it comes to interface handling, you can actually get around an interface as outlined in the bug report I filed here: http://bugs.php.net/bug.php?id=48376
This unfortunately exists up to 5.3.0 for certain (though I'm not 100% certain if its been corrected with the 5.3.1), and 6.0.0 Dev beta. Since it allows you to decay the scope of you're interface, this results in intermittent runtime errors instead of compilation errors. It is indicated by the php team that this is not a bug, which is total bs I'm afraid.
Too bad too, interfaces are probably the single most powerful tool when it comes to OOP since it is the only way in PHP (and most other OO languages I've used, except for C++) to control multiple inheritance.

Dormilich
03-03-2010, 03:38 PM
However, PHP fails when it comes to interface handling, you can actually get around an interface as outlined in the bug report I filed here: http://bugs.php.net/bug.php?id=48376

I didn’t understand that bug. I always thought that calling __construct() happens, whenever the object is created through new*, regardless of public/protected/private. that only matters, if you explicitly access it as ClassName::__construct() or parent::__construct(). And I can’t spot the relation to Interfaces …

* that’s why the Singleton Pattern explicitly declares empty constructors.

Fou-Lu
03-03-2010, 04:11 PM
Yes, __construct or a method with the same name as the class will always be called when using new if available. Otherwise, it will always look for its parent. Using a non-public construct will always throw an error on a new call. In PHP, I'm fairly certain that even constructors cannot be decayed from inherited methods, though they are intending us to be able to do this in the future. I'm pretty certain offhand that inherited classes can however redeclare the __construct signature. Singletons should use private / protected constructors since you cannot reassign $this (you could get around this issue if you could assign $this = self::$instance; for example). Otherwise, the singleton pattern will not allow you to act on a single instance of an object since each new will create a new instance of that class.

The first example was showing scope decay, the second shows the decay of interfaced methods as well - look lower to see the interfaced example (I'll include it here):



interface IInterface
{
public function test();
}

class Grandfather
{
private function __construct()
{
}

private function test() //<-- This allows us to decay
{
printf("%s\n", __METHOD__);
}
}

class Father extends Grandfather implements IInterface
{
public function __construct()
{
printf("%s\n", __METHOD__);
}

public function test() // <-- Required by IInterface
{
printf("%s\n", __METHOD__);
}
}

class Son extends Father
{
protected function __construct()
{
parent::__construct();
printf("%s\n", __METHOD__);
}

public static function createInstance()
{
return new self;
}

protected function test() // <-- This is not right, you cannot decay a parent scope
{
parent::test();
printf("%s\n", __METHOD__);
}
}


class CorrectScope implements IInterface
{
public function __construct()
{
printf("%s\n", __METHOD__);
}

public function test()
{
printf("%s\n", __METHOD__);
}
}

$t = Son::createInstance();
var_dump($t);
if ($t instanceof IInterface)
{
print("\$t is an IInterface, check test:\n");
// Fatal error on an interface call
printf("Results of \$t->test() = %s\n", $t->test());
}


When run, $t indicates that it is an interface of type IInterface which should guarentee that a method called test exists within it. However, attempting to call this method on this interfaced class will throw an uncatchable fatal error.

I have a much better version of this with a runner and stuff if you want to see, but you'll need to wait until I at least get a new monitor at home :P

Dormilich
03-03-2010, 10:48 PM
Otherwise, it will always look for its parent.
thatís news to me.


When run, $t indicates that it is an interface of type IInterface which should guarentee that a method called test exists within it. However, attempting to call this method on this interfaced class will throw an uncatchable fatal error.
what I find strange here is that the error is not a parse error (overwriting public with proteced), although you could argue, that the parentís method still conforms to the interface, but due to overwriting itís not publicly accessible.

Fou-Lu
03-03-2010, 11:24 PM
Yes exactly!
The error should be a parse error at compile time, not runtime. Extending a base class cannot decay the scope of a parent method, otherwise its not really an extension on it since it then defeats the purpose of conforming to the dynamic binding model (Son is typeof Father, so must include all methods expected of Father). The error should be occuring at linking and not at runtime, but as shown it will trigger at runtime instead which of course results in intermittent uncatachable fatal errors.
You'll also notice if you take the grandfather class out of the equation completely, that the code would run as expected. This only exists in a Grandfather > Father > Son relationship.

As for the constructor:


Note: Parent constructors are not called implicitly if the child class defines a constructor. In order to run a parent constructor, a call to parent::__construct() within the child constructor is required.


parent::__construct will be called implicitly if extending a parent class and no constructor is defined for the child. Otherwise, unlike languages like Java, PHP requires us to explicitly call the parent (java implicilty calls the parent constructor, and if overriden to use an overloaded constructor it must be the first line). I actually really like this feature of PHP. Eventually we'll be able to control the scope on this too; that I am looking forward to.

Dormilich
03-04-2010, 12:48 AM
parent::__construct will be called implicitly if extending a parent class and no constructor is defined for the child.

„Man kann alt werden, wie ’ne Kuh und lernt immernoch dazu.“ (german proverb, directly translated as "you can get as old as a sphinx, but you can still learn new things")

Joseph Witchard
03-04-2010, 05:32 AM
So should I or should I not use interfaces (provided I come to a place where they would be useful)?

Dormilich
03-04-2010, 07:29 AM
you should.

but you should also be aware, where there could be problems, but that’s rather rare.

Joseph Witchard
03-04-2010, 09:03 AM
So you just declare the functions in the interface? You don't actually tell them what to do until you make a class that implements the interface?

Dormilich
03-04-2010, 10:03 AM
basically, yes

Object interfaces allow you to create code which specifies which methods a class must implement, without having to define how these methods are handled.

Fou-Lu
03-04-2010, 03:33 PM
So you just declare the functions in the interface? You don't actually tell them what to do until you make a class that implements the interface?

Yeah, personally I feel that interfacing is at least 100x more powerful than a standard extension. Interfaces are the only way to simulate multiple inheritance in most languages. The example showing this was the use of the POSTWrapper, it implemented both the IRequest and IResponse interfaces, so postwrapper itself can be treated as either an irequest or iresponse datatype. Although PHP is a datatype weak language, there are two exceptions to this rule that you can handle in any method/function:


[public ]function myFunction(array $aVals); // not the same as ArrayObject though
// And
[public ]function myFunction(MyClass $mc);


PHP lets you typehint the values of you're parameters for only array and class types. This lets you introduce an OO approach to you're code while still maintaining control on you're datatypes (with ease). Typehinting became available as of PHP5.


Best way to think of an Interface is its simply a skeleton contract for forcing specific methods (except as I've shown in the bug above, but yes thats an unusual situation; it bugs me more that they won't identify it as a bug). Implementation details are irrelevant, for this we can create abstract and concrete methods to handle them. This are also useful for what we call adapters:


interface IExecute
{
public function preExecute();
public function execute();
public function postExecute();
}

Now, we have an interface for something that can execute. But perhaps we don't always want the pre and post execution to happen:


abstract class ExecuteAdapter implements IExecute
{
public function preExecute()
{
}

public function postExecute()
{
}
}


Now, we extend ExecuteAdapter into a concrete class, we are now only required to implement the execute() method in our concrete class. Pre and post execute can be called, and will silently do nothing. I would actually toss a custom NotImplementedException from there. However, we would still typehint for the IExecute interface instead of the concrete or abstract class so long as it fits our rulesets. The above ExecuteAdapter class is just a shortcut; perhaps we want an IExecute class to exist, but it must instead extend another class (for simplicity of that class), so it would then need to implement the IExecute and add all three of those methods.

That makes sense? OO theory is one of those things that just click and then you understand it. After that, its a matter of learning the language in particular. You may want to consider looking at either Java or C# (doesn't matter, they are so close in usage that half the time you cannot be 100% sure which is in use hah) to learn OO with. That will show you how to properly control datatype handling without the need of the gritty C/C++ allocations and frees.

Dormilich
03-04-2010, 03:39 PM
(except as I've shown in the bug above, but yes thats an unusual situation; it bugs me more that they won't identify it as a bug).

Iíd say the bugís description was not obvious enough. I didnít grasp its idea, unless you explained it.



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum