Tutorials >
PHP >
OOP (Object Oriented Programming)
|
Gepost op: 03 januari 2014 - 15:38 |
|
|
|
PHP expert
|
De backslashes zijn bij de namespaces weggevallen. |
|
|
|
Gepost op: 03 januari 2014 - 20:44 |
|
|
|
Moderator
|
Hoi Joost, dank je wel voor het melden. Dit klopte inderdaad en als het goed is heb ik dit nu aangepast.
Tijdens het converteren naar een php pagina is dit fout gegaan. Nogmaals bedankt voor het melden. Als je nog inhoudelijke of andere opmerkingen hebt hoor ik het graag. |
|
|
|
Gepost op: 04 januari 2014 - 21:33 |
|
|
|
Moderator
|
Object Orientated Programming? Die kende ik nog niet .
Wijnand schreef: Use DevModulesAccountsUser as Account;
Door de backslash geef je aan dat deze namespace buiten de huidige namespace zit en dat je de hele namespace-naam gaat opgeven van de class die je wilt selecteren. Door 'as' te gebruiken geef je aan dat je die class 'User' (van de betreffende namespace) met de naam 'Account' wil gebruiken.
Maar vervolgens gebruik je in het voorbeeld User:: . Zou dat niet Account:: moeten zijn? Het idee van het gebruik van "as" is toch het definiëren van een alias als ik het goed begrijp? Niet echt zinnig als je die dan vervolgens niet gebruikt .
Over __get()
Ik zou toch een soort van controle inbouwen die kijkt of de property bestaat? Dat zou je kunnen doen in combinatie met __isset(), dus bijvoorbeeld met if ($this->__isset($key)) of zelfs if (self::__isset($key)) (EDIT: als je een object van een class gebruikt is $this beter/intuitiever; als je static properties set/get/controleert op bestaan gebruik je self:: )? En anders die() / throw exception of wat dan ook.
Over __isset()
Het voorbeeld klopt volgens mij niet helemaal? Die accepteert namelijk alles (return true lol). Als de Test class voortborduurt op de eerdere variant waarin __get() en __set() worden gebruikt zou dit dus zoiets moeten worden (met hierbij ook aangepaste __get()):
<?php
class Test {
...
public function __isset($key) {
return isset($this->columns[$key]);
}
...
public function __get($key) {
if ($this->__isset($key)) {
return $this->columns[$key];
} else {
// complain!
// die(), throw new Exception() or whatever.
}
}
...
}
?>
<?php class Test { ... public function __isset($key) { return isset($this->columns[$key]); } ... public function __get($key) { if ($this->__isset($key)) { return $this->columns[$key]; } else { // complain! // die(), throw new Exception() or whatever. } } ... } ?>
Variable aantal opties
Vaak wil je bij de creatie van een object allerlei (waarden van) eigenschappen meegeven, maar het kan handig zijn als je deze niet hoeft in te stellen als deze al een default waarde hebben. Door wat truuks met arrays kun je dit eenvoudig doen:
<?php
class LotsOfProperties
{
protected $properties;
public function __construct($properties=array()) {
$this->properties = $properties + self::getDefaultProperties(); // first overwrites second
}
public function getProperties() {
return $this->properties;
}
protected static function getDefaultProperties() {
return array(
'width' => 640,
'height' => 480,
'mime' => 'image/gif',
'colors' => 16,
);
}
}
// Everything with default settings.
$test = new LotsOfProperties();
echo '<pre>'.print_r($test->getProperties(), true).'</pre>'; // not safe, still needs escaping
// prints:
/*
Array
(
[width] => 640
[height] => 480
[mime] => image/gif
[colors] => 16
)
*/
// Custom width and colors.
$test = new LotsOfProperties(array('width' => 800, 'colors' => 256));
echo '<pre>'.print_r($test->getProperties(), true).'</pre>'; // not safe, still needs escaping
// prints
/*
Array
(
[width] => 800
[colors] => 256
[height] => 480
[mime] => image/gif
)
*/
?>
<?php class LotsOfProperties { protected $properties; public function __construct ($properties=array()) { $this->properties = $properties + self::getDefaultProperties(); // first overwrites second } public function getProperties() { return $this->properties; } protected static function getDefaultProperties () { 'width' => 640, 'height' => 480, 'mime' => 'image/gif', 'colors' => 16, ); } } // Everything with default settings. $test = new LotsOfProperties(); echo '<pre>'.print_r($test->getProperties(), true).'</pre>'; // not safe, still needs escaping // prints: /* Array ( [width] => 640 [height] => 480 [mime] => image/gif [colors] => 16 ) */ // Custom width and colors. $test = new LotsOfProperties (array('width' => 800, 'colors' => 256)); echo '<pre>'.print_r($test->getProperties(), true).'</pre>'; // not safe, still needs escaping // prints /* Array ( [width] => 800 [colors] => 256 [height] => 480 [mime] => image/gif ) */ ?>
De "+" operator op arrays doet het volgende: als A en B arrays zijn en je doet C = A + B, dan zullen alle values bij alle keys die in A en B voorkomen worden overschreven met de waarden van A (in resultaat-array C)... Onthoud gewoon "de eerste overschrijft de tweede" (first overwrites second).
Je zou natuurlijk ook nog kunnen controleren of de properties die je probeert in te stellen geldig zijn, je zou dus ook nog een whitelist (met typechecks, als je dat leuk vindt) kunnen meenemen.
abstract classes en interfaces
Het verschil tussen deze twee snap ik zelf ook nog niet helemaal, maar volgens mij is het zoiets:
Zoals een Class een blauwdruk is voor een Object, is een abstract Class een blauwdruk voor een andere Class die hiervan afgeleid is.
Als een Class ook maar één abstractie property of method heeft, wordt daarmee de hele Class abstract (moet dan als zodanig gedefinieerd worden).
Een abstract Class heeft meestal al enkele methoden geimplementeerd (of heeft al enkele properties gedefinieerd), andere moeten in de afgeleide Class(es) geimplementeerd worden.
Een interface is meer een soort van voorschrift. In een interface implementeer je niets, je definieert alleen maar methoden. Als een andere klasse een interface implementeert, dan MOETEN al deze interface-methoden hier in zitten, eigenlijk hetzelfde dus als abstracte methoden... Een interface schrijft voor WAT er geimplementeerd moet worden, maar niet HOE.
Een ander verschil tussen interfaces en abstracte Classes is dat een Class slechts een afgeleide kan zijn van één hoger gelegen (en mogelijk abstracte) Class (Class A extends B), maar tegelijkertijd méérdere interfaces kan implementeren (Class A extends B implements X, Y, Z).
Stel bijvoorbeeld dat je een ResultSet interface hebt in je database abstractie laag. Deze beschrijft dus welke methoden alle afgeleide classes zouden moeten implementeren. Maar elke implementatie is specifiek voor het database-type. Je zou dan dus zoiets kunnen doen (correct me if I'm wrong):
<?php
// This is what we expect to be implemented in any database-type resultset functionality:
interface DatabaseResult
{
public function fetchRow();
public function fetchValue();
public function numRows();
public function dataSeek($offset);
public function freeResult();
}
// This is a (uniform) database layer implementation for the the (specific) MySQLi variant, we use the functionality that the MySQLi class already offers, we extend upon it!
class DatabaseResultMySQLi extends MySQLi_Result implements DatabaseResult
{
// Returns an associative array.
public function fetchRow() {
return $this->fetch_assoc();
}
// Returns a single value, for COUNT queries and such.
public function fetchValue() {
$row = $this->fetch_row();
return $row[0];
}
public function numRows() {
return $this->num_rows;
}
public function dataSeek($offset) {
return $this->data_seek($offset);
}
public function freeResult() {
$this->free();
}
}
?>
<?php // This is what we expect to be implemented in any database-type resultset functionality: interface DatabaseResult { public function fetchRow(); public function fetchValue(); public function numRows(); public function dataSeek($offset); public function freeResult(); } // This is a (uniform) database layer implementation for the the (specific) MySQLi variant, we use the functionality that the MySQLi class already offers, we extend upon it! class DatabaseResultMySQLi extends MySQLi_Result implements DatabaseResult { // Returns an associative array. public function fetchRow() { return $this->fetch_assoc(); } // Returns a single value, for COUNT queries and such. public function fetchValue() { $row = $this->fetch_row(); return $row[0]; } public function numRows() { return $this->num_rows; } public function dataSeek($offset) { return $this->data_seek($offset); } public function freeResult() { $this->free(); } } ?>
|
|
|
|
Gepost op: 05 januari 2014 - 10:36 |
|
|
|
Moderator
|
Je kunt ook parent-child (boom)structuren inbouwen in een class, bijvoorbeeld een geneste div-structuur:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>nested divs</title>
</head>
<body style="margin: 50px; font-family: sans-serif;">
<?php
class NestedDiv
{
protected $name;
protected $style;
protected $parent;
protected $buffer;
protected $children;
public function __construct($name='', $style='', $parent=null) {
$this->name = $name;
$this->style = $style;
$this->parent = $parent;
$this->children = array();
if ($parent) {
$parent->addChild($this);
}
}
protected function addChild($object) {
$this->children[] = $object;
}
public function printDiv() {
if (count($this->children)) {
// buffer lower levels
ob_start();
foreach ($this->children as $child) {
$child->printDiv();
}
$this->buffer = ob_get_clean();
}
// print current level
?><div style="<?php echo $this->style ?>">
<?php
// did we have multiple (floating) children? (your styles should reflect this too, obviously)
if (count($this->children) > 1) {
// print the name, then add a wrapper and clear div
echo $this->name;
?><div><?php
if (count($this->children)) {
echo $this->buffer;
}
?><div style="clear: both;"><!-- empty --></div>
</div><?php
} else {
// otherwise just print stuff
echo $this->name;
if (count($this->children)) {
echo $this->buffer;
}
}
?></div><?php
}
}
// root div
$rootdiv = new NestedDiv('one', 'width: 90%; padding: 5%; background-color: #f0f0ff;');
// child div with $rootdiv as parent
$div2 = new NestedDiv('two', 'width: 90%; padding: 5%; background-color: #d0d0ff;', $rootdiv);
// two floating divs on the same level, both having $div2 as parent
$div3_1 = new NestedDiv('three left', 'float: left; width: 40%; padding: 5% 4% 5% 5%; margin: 0 1% 0 0; background-color: #b0b0ff;', $div2);
$div3_2 = new NestedDiv('three right', 'float: left; width: 40%; padding: 5% 5% 5% 4%; margin: 0 0 0 1%; background-color: #b0b0ff;', $div2);
// a child div that has $div3_2 (the right floating div) as parent
$div4 = new NestedDiv('four right', 'width: 90%; padding: 5%; background-color: #9090ff;', $div3_2);
// print the ROOT div inside a box with an absolute width)
?><div style="width: 800px">
box
<?php echo $rootdiv->printDiv() ?>
</div>
</body>
</html>
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>nested divs</title> </head> <body style="margin: 50px; font-family: sans-serif;"> <?php class NestedDiv { protected $name; protected $style; protected $parent; protected $buffer; protected $children; public function __construct($name='', $style='', $parent=null) { $this->name = $name; $this->style = $style; $this->parent = $parent; $this->children = array(); if ($parent) { $parent->addChild($this); } } protected function addChild($object) { $this->children[] = $object; } public function printDiv() { if (count($this->children)) { // buffer lower levels foreach ($this->children as $child) { $child->printDiv(); } } // print current level ?><div style=" <?php echo $this->style ?>"> <?php // did we have multiple (floating) children? (your styles should reflect this too, obviously) if (count($this->children) > 1) { // print the name, then add a wrapper and clear div ?><div><?php if (count($this->children)) { } ?><div style="clear: both;"><!-- empty --></div> </div><?php } else { // otherwise just print stuff if (count($this->children)) { } } ?></div><?php } } // root div $rootdiv = new NestedDiv('one', 'width: 90%; padding: 5%; background-color: #f0f0ff;'); // child div with $rootdiv as parent $div2 = new NestedDiv('two', 'width: 90%; padding: 5%; background-color: #d0d0ff;', $rootdiv); // two floating divs on the same level, both having $div2 as parent $div3_1 = new NestedDiv('three left', 'float: left; width: 40%; padding: 5% 4% 5% 5%; margin: 0 1% 0 0; background-color: #b0b0ff;', $div2); $div3_2 = new NestedDiv('three right', 'float: left; width: 40%; padding: 5% 5% 5% 4%; margin: 0 0 0 1%; background-color: #b0b0ff;', $div2); // a child div that has $div3_2 (the right floating div) as parent $div4 = new NestedDiv('four right', 'width: 90%; padding: 5%; background-color: #9090ff;', $div3_2); // print the ROOT div inside a box with an absolute width) ?><div style="width: 800px"> box <?php echo $rootdiv->printDiv() ?> </div> </body> </html>
EDIT: In dit voorbeeld is het niet echt nodig om output te bufferen, maar stel nu dat in de printDiv-methode (die dan waarschijnlijk anders zou heten) informatie doorgeeft aan een bovengelegen div? Als je geen output buffering zou gebruiken zou dit niet kunnen, immers de bovengelegen div is al afgedrukt, je kunt die dan niet meer aanpassen.
Maar stel nu dat je dit voorbeeld doortrekt naar de volgende situatie: je buitenste div is een "maintemplate" van een webpagina en een lager gelegen div is code die je op specifieke pagina's uitvoert, bijvoorbeeld artikel-pagina's. Je zou dan met deze buffertechniek nog zaken kunnen aanpassen in het maintemplate. Denk hierbij aan het toevoegen van verwijzingen naar CSS- of JavaScript-bestanden of het instellen van de <title>-tag van de maintemplate op grond van de titel van een artikel. |
|
|
|
Gepost op: 21 januari 2014 - 10:20 |
|
|
|
Moderator
|
Bedankt voor je opmerkingen. Volgens mij heb ik de dingen die echt fout waren (die jij aangaf) eruit gehaald. De voorstellen, daar ga ik verder over na denken. Het klopt inderdaad dat interfaces 'slechts' voor structuur zijn, dat je daar geen gegevens in kunt vullen, maar dat het een blauwdruk is van de dingen die perse in een bepaalde class moeten. |
|
|
|
Gepost op: 12 augustus 2014 - 12:39 |
|
|
|
Moderator
|
|
|
|
Gepost op: 30 januari 2015 - 14:01 |
|
|
|
Nieuw lid
|
Het is een kleinigheid, een tikfout. Scope Resulation Object moet zijn Scope Resolution Object |
|
|
Enkel aanvullende informatie is welkom. Geen prijzende of afkeurende reacties. |
|
|
|