[PHP][OOP] Instanties van klassen

Pagina: 1
Acties:
  • 168 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Hallo PHP-ers,

Allereerst, wat algemene informatie:
PHP-versie: 4.3.11
PHP-config: Standaard php.ini (Fedora 3 installatie)

Ik ben zelf redelijk bekend met java en de principes van OOP. Ik ben een webapplicatie aan het ontwikkelen, althans, voorlopig nog de basis. Dit doe ik in PHP, vooral omdat ik afhankelijk ben van andere mensen. Ik wil de webapplicatie object georienteerd maken, omdat ik dit principe erg goed vind en simpelweg omdat dit een groot project gaat worden (in de zin van hoeveelheid code).

Maargoed, to the point:

STEL we hebben 2 klassen, namelijk dbConnection en errorLogger (dit is fictief dus). In de klasse dbConnection wil ik dat errors afgehandeld worden door errorLogger. Dit betekent dat dbConnection een instantie moet 'hebben' van errorLogger. Hoe gebeurt dit in PHP? Ik wil niet een nieuwe instantie van errorLogger aanmaken in dbConnection.

Het volgende heb ik geprobeerd, en werkt niet:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?
class errorLogger{ *wat hier gebeurt doet er niet toe* }

class dbConnection{
   var $eLogger;
   function dbConnection($logingegegevens_enzo,$eLogger){
      $this->eLogger = $eLogger;
      $this->eLogger->log("een error"); // Ja, de functie 'log' bestaat
   }
}

$eLogger = new errorLogger();
$dbcon = new dbConnection("inloggegens enzo",$eLogger);


Ik neem aan dat hier een standaard manier voor is in PHP. Ik heb google afgespeurd naar goede tutorials, maar bijna al deze tutorials behandelen slechts de basis, ook php.net.

Ik hoop dat iemand mij kan helpen, alvast bedankt.

Erik

Acties:
  • 0 Henk 'm!

  • _eXistenZ_
  • Registratie: Februari 2004
  • Laatst online: 19-09 01:03
PHP werkt met globals. Je moet eventjes je inlezen in PHP want dat werkt best anders dan Java.

There is no replacement for displacement!


Acties:
  • 0 Henk 'm!

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
Verwijderd schreef op dinsdag 17 april 2007 @ 17:23:
Hallo PHP-ers,

Allereerst, wat algemene informatie:
PHP-versie: 4.3.11
PHP-config: Standaard php.ini (Fedora 3 installatie)

Ik ben zelf redelijk bekend met java en de principes van OOP. Ik ben een webapplicatie aan het ontwikkelen, althans, voorlopig nog de basis. Dit doe ik in PHP, vooral omdat ik afhankelijk ben van andere mensen. Ik wil de webapplicatie object georienteerd maken, omdat ik dit principe erg goed vind en simpelweg omdat dit een groot project gaat worden (in de zin van hoeveelheid code).

Maargoed, to the point:

STEL we hebben 2 klassen, namelijk dbConnection en errorLogger (dit is fictief dus). In de klasse dbConnection wil ik dat errors afgehandeld worden door errorLogger. Dit betekent dat dbConnection een instantie moet 'hebben' van errorLogger. Hoe gebeurt dit in PHP? Ik wil niet een nieuwe instantie van errorLogger aanmaken in dbConnection.

Het volgende heb ik geprobeerd, en werkt niet:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?
class errorLogger{ *wat hier gebeurt doet er niet toe* }

class dbConnection{
   var $eLogger;
   function dbConnection($logingegegevens_enzo,$eLogger){
      $this->eLogger = $eLogger;
      $this->eLogger->log("een error"); // Ja, de functie 'log' bestaat
   }
}

$eLogger = new errorLogger();
$dbcon = new dbConnection("inloggegens enzo",$eLogger);


Ik neem aan dat hier een standaard manier voor is in PHP. Ik heb google afgespeurd naar goede tutorials, maar bijna al deze tutorials behandelen slechts de basis, ook php.net.

Ik hoop dat iemand mij kan helpen, alvast bedankt.

Erik
Wat je hier zoekt is het Singleton-pattern. Dit is een van de meest bekende Design Patterns. Zie ook: Singleton Pattern. Sowieso is het echt een boek dat je moet lezen als je OO programmeert.

Zie ook: Five Common Design Patterns in PHP

Acties:
  • 0 Henk 'm!

  • CodeCaster
  • Registratie: Juni 2003
  • Niet online

CodeCaster

Can I get uhm...

Je kunt een handle meegeven., maar ik dacht dat dat pas vanaf PHP5 werkt...

https://oneerlijkewoz.nl
Op papier is hij aan het tekenen, maar in de praktijk...


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Het lijkt er erg op dat PHP4 weinig mogelijkheden heeft wat dit betreft. (Na de links uit bovenstaande reacties doorgewerkt te hebben.) Helaas heb ik niet de mogelijkheid om te updaten naar PHP5. Echter, het lijkt mij onlogisch dat PHP4 wel het gebruik van klassen ondersteunt, maar geen enkele manier heeft om hier op de door mij beschreven manier mee om te gaan (niet letterlijk mijn manier, maar een soortgelijke). Is er dan echt geen oplossing voor in PHP4?

Acties:
  • 0 Henk 'm!

  • robbert
  • Registratie: April 2002
  • Laatst online: 20:37
Verwijderd schreef op dinsdag 17 april 2007 @ 17:23:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?
class errorLogger{ *wat hier gebeurt doet er niet toe* }

class dbConnection{
   var $eLogger;
   function dbConnection($logingegegevens_enzo,&$eLogger){
      $this->eLogger = &$eLogger;
      $this->eLogger->log("een error"); // Ja, de functie 'log' bestaat
   }
}

$eLogger = new errorLogger();
$dbcon = new dbConnection("inloggegens enzo",$eLogger);
Je wil geen kopie van die ErrorLogger in je database klasse, dan moet je hem als reference meegeven. Php5 doet dit overigens automatisch bij klassen en arrays.

[ Voor 10% gewijzigd door robbert op 17-04-2007 18:01 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
robbert, dit is toch juist wat ik doe in mijn voorbeeld? Alleen niet op de goede manier blijkbaar en mijn vraag is dus wat wel de goede manier is :)

Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Dat wordt dus zoiets:
PHP:
1
2
3
4
5
6
7
8
9
class dbConnection{
   function dbConnection($logingegegevens_enzo){
      global $eLogger;
      $eLogger->log('een error'); // Ja, de functie 'log' bestaat
   }
}

$eLogger = new errorLogger();
$dbcon = new dbConnection("inloggegens enzo");


Maar ik zie dat je al een betere oplossing hebt...

Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 22:47
PHP:
1
  $this->eLogger = &$eLogger;


Hier gaat het fout denk ik - PHP references zijn namelijk niet hetzelfde als C++ references, als je ze als functieargument meegeeft hoef je ze dan ook niet te dereferencen zoals je gewend bent in C++. Bij nader inzien klopt't toch wel aangezien je anders bij de toewijzing in je class zelf alsnog een kopie maakt :)

[knip]

Overigens, probeer alsjeblieft te upgraden naar PHP5 als je kan - niet alleen krijg je dan veel meer en betere OOP mogelijkheden, 't heeft ook hippe meuk als een exception handler etc. Met PHP4.3 kun je wel OO werken, maar makkelijk is anders :)

//edit
Nevermind, 'k zie nu pas dat robbert die references heeft meegegeven, sneak devil, dingen in quotes aanpassen is nooit grappig :| :+

[ Voor 46% gewijzigd door FragFrog op 17-04-2007 20:16 ]

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • robbert
  • Registratie: April 2002
  • Laatst online: 20:37
Verwijderd schreef op dinsdag 17 april 2007 @ 18:09:
robbert, dit is toch juist wat ik doe in mijn voorbeeld? Alleen niet op de goede manier blijkbaar en mijn vraag is dus wat wel de goede manier is :)
Ik heb wat aanpassingen gemaakt in de quote :) Kijk daar eens naar :)

Acties:
  • 0 Henk 'm!

  • robbert
  • Registratie: April 2002
  • Laatst online: 20:37
FragFrog schreef op dinsdag 17 april 2007 @ 19:08:
PHP:
1
2
3
function dbConnection(& $eLogger) {
  $this->eLogger = $eLogger; 
}
Volgens mij moet je er het volgende van maken:
PHP:
1
2
3
function dbConnection(& $eLogger) {
  $this->eLogger = &$eLogger; 
}

Anders maakt die alsnog een kopie.
//edit
Nevermind, 'k zie nu pas dat robbert die references heeft meegegeven, sneak devil, dingen in quotes aanpassen is nooit grappig :| :+
Die 2 & tekens waren inderdaad nogal subtiel :P
Daos schreef op dinsdag 17 april 2007 @ 18:33:
[...]

Dat wordt dus zoiets:
PHP:
1
2
3
4
5
6
7
8
9
class dbConnection{
   function dbConnection($logingegegevens_enzo){
      global $eLogger;
      $eLogger->log('een error'); // Ja, de functie 'log' bestaat
   }
}

$eLogger = new errorLogger();
$dbcon = new dbConnection("inloggegens enzo");


Maar ik zie dat je al een betere oplossing hebt...
global is een van de meest ranzige dingen... Als je iets in object georiënteerd programmeren moet vermijden is het global wel.

[ Voor 33% gewijzigd door robbert op 17-04-2007 19:56 ]


Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 22:47
robbert schreef op dinsdag 17 april 2007 @ 19:55:
Volgens mij moet je er het volgende van maken:
PHP:
1
2
3
function dbConnection(& $eLogger) {
  $this->eLogger = &$eLogger; 
}

Anders maakt die alsnog een kopie.
Nu ik er zo nog eens naar kijk heb je inderdaad gelijk, anders maak je een kopie bij het toewijzen van de variabele aan je class, doh :) Maargoed, zoals ik al editte, ik ging er eerst vanuit dat het niet werkte zoals jij quotte, er niet bij stilstaande dat je de quote had geedit 8)7

En inderdaad, globals zijn ranzig :+

[ Voor 14% gewijzigd door FragFrog op 17-04-2007 20:15 ]

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
Wat je dus wil doen is errorLogger een Singleton maken, en dan kan iedereen een instance van de Singleton opvragen. Dat is dé manier om het te doen als je met OO werkt.

Acties:
  • 0 Henk 'm!

  • OxiMoron
  • Registratie: November 2001
  • Laatst online: 08-07 14:27
Kijk toch eens of je kunt updaten naar php5, dat werkt een stuk beter voor OO dan php4.

Albert Einstein: A question that sometime drives me hazy: Am I or are the others crazy?


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Na lang overleg heb ik de andere mensen weten te overtuigen van het nut van updaten naar php5. Vanaf nu draai ik dus php5. Ik ga het proberen!

Acties:
  • 0 Henk 'm!

  • WouZz
  • Registratie: Mei 2000
  • Niet online

WouZz

Elvis is alive!

Singleton maken lukt ook in PHP4 hoor:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class DbConnection
{
    var $logger;
    
    // php4 constructor
    function DbConnection($params)
    {
        $this->__construct($params);
    }
    
    // php5 constructor
    function __construct($params)
    {
        $this->logger =& ErrorLogger::getInstance();
        $this->connect($params);
    }
    
    function connect($params)
    {
        $this->logger->info('Connecting to database...');
        // etc..
    }
}


PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ErrorLogger
{
    function ErrorLogger()
    {
        $this->__construct();
    }
    
    function __construct() {}
    
    function &getInstance()
    {
        static $theInstance;
        
        if(!isset($theInstance))
            $theInstance = new ErrorLogger();
        
        return $theInstance;
    }
    
    function info($message)
    {
        print $message;
    }
}

On track


Acties:
  • 0 Henk 'm!

  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

Het idee achter singleton pattern is dat er maar sprake mag zijn van 1 instantie. Wanneer een constructor public is, of laat ik het zo zeggen, wanneer je een constructor niet private kan maken, kan iedereen een instantie maken van dat object en is er eigenlijk al geen sprake meer van een singleton. Daarbij wouZz, om jouw getInstance aan te roepen moet je een instantie al hebben van ErrorLogger toch? De methode getInstance is namelijk niet static, en daarmee niet klassengebonden maar instantiegebonden.

Acties:
  • 0 Henk 'm!

  • robbert
  • Registratie: April 2002
  • Laatst online: 20:37
prototype schreef op donderdag 19 april 2007 @ 14:59:
Het idee achter singleton pattern is dat er maar sprake mag zijn van 1 instantie. Wanneer een constructor public is, of laat ik het zo zeggen, wanneer je een constructor niet private kan maken, kan iedereen een instantie maken van dat object en is er eigenlijk al geen sprake meer van een singleton.
Zo kun je ook direct zeggen dat er helemaal geen spake is van OOP in php4. Je kan stiekem toch gewoon de interne structuur van de klasse verprutsen.
Daarbij wouZz, om jouw getInstance aan te roepen moet je een instantie al hebben van ErrorLogger toch? De methode getInstance is namelijk niet static, en daarmee niet klassengebonden maar instantiegebonden.
Je kan die methode gewoon als static zijnde gebruiken, het static keyword bestond nog niet in php4.

[ Voor 10% gewijzigd door robbert op 19-04-2007 18:23 ]


Acties:
  • 0 Henk 'm!

  • SchizoDuckie
  • Registratie: April 2001
  • Laatst online: 18-02 23:12

SchizoDuckie

Kwaak

Als je met php5 werkt ben je toch met 1 static functie in je error logger class klaar?

je aanroep wordt dan :
PHP:
1
   $eLogger::log('een error'); // Ja, de functie 'log' bestaat 

Stop uploading passwords to Github!


Acties:
  • 0 Henk 'm!

  • robbert
  • Registratie: April 2002
  • Laatst online: 20:37
SchizoDuckie schreef op donderdag 19 april 2007 @ 18:22:
Als je met php5 werkt ben je toch met 1 static functie in je error logger class klaar?

je aanroep wordt dan :
PHP:
1
   $eLogger::log('een error'); // Ja, de functie 'log' bestaat 
Dan is het tot mijn weten nog het volgende:
PHP:
1
   ErrorLogger::log('een error'); // Ja, de functie 'log' bestaat 

Een static functie hoort namelijk niet bij een instantie van die klasse.

Maar dit is ook niet echt handig, waar slaat die dan ergens de errors op? Ook in static variabelen, niet echt mooi imho.

Acties:
  • 0 Henk 'm!

  • SchizoDuckie
  • Registratie: April 2001
  • Laatst online: 18-02 23:12

SchizoDuckie

Kwaak

robbert schreef op donderdag 19 april 2007 @ 18:25:
[...]

Dan is het tot mijn weten nog het volgende:
PHP:
1
   ErrorLogger::log('een error'); // Ja, de functie 'log' bestaat 

Een static functie hoort namelijk niet bij een instantie van die klasse.

Maar dit is ook niet echt handig, waar slaat die dan ergens de errors op? Ook in static variabelen, niet echt mooi imho.
Uh jah idd je hebt gelijk, was even vergeten die var te veranderen in de classname verder kan de static class eventueel files wegschrijven in je logfile of gelijk naar firebug gooien ofzo, handelen iig :)

Stop uploading passwords to Github!


Acties:
  • 0 Henk 'm!

  • robbert
  • Registratie: April 2002
  • Laatst online: 20:37
Maar ik vind hier een singleton overigens helemaal niet fijn voor. Als het netjes OOP wil doen, maak je een interface ErrorLogger, vervolgens kun je hier verschillende implementaties van maken. Bijvoorbeeld 1 die het dus naar een bestand wegschrijft, 1 die het in een database opslaat etc. Een instantie van die implementatie wil ik vervolgens gebruiken in mijn programma.
Met een singleton is het niet mogelijk om een DatabaseErrorLogger ipv een FileErrorLogger te gebruiken. Dan moet je overal die aanroepen van:
PHP:
1
DatabaseErrorLogger::getInstance()
veranderen naar
PHP:
1
FileErrorLogger::getInstance()

TS zijn oorspronkelijke idee, enkel dan met references lijkt mij dan nog het handigste.

[ Voor 30% gewijzigd door robbert op 19-04-2007 18:59 ]


Acties:
  • 0 Henk 'm!

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
robbert schreef op donderdag 19 april 2007 @ 18:56:
Maar ik vind hier een singleton overigens helemaal niet fijn voor. Als het netjes OOP wil doen, maak je een interface ErrorLogger, vervolgens kun je hier verschillende implementaties van maken. Bijvoorbeeld 1 die het dus naar een bestand wegschrijft, 1 die het in een database opslaat etc. Een instantie van die implementatie wil ik vervolgens gebruiken in mijn programma.
Met een singleton is het niet mogelijk om een DatabaseErrorLogger ipv een FileErrorLogger te gebruiken. Dan moet je overal die aanroepen van:
PHP:
1
DatabaseErrorLogger::getInstance()
veranderen naar
PHP:
1
FileErrorLogger::getInstance()

TS zijn oorspronkelijke idee, enkel dan met references lijkt mij dan nog het handigste.
Ja, maar om iedere keer al die references mee te geven is ook niet echt netjes, mooi en efficient.

Hier is ook een oplossing voor: Abstract Factory.

Als je serieus van plan bent om meerdere loggers aan te maken is dat handig, zo niet, dan kan je beter gewoon een Singleton gebruiken. Het heeft geen zin om te over-designen. Zodra je merkt dat je twee verschillende soorten loggers gebruikt zou ik wel zeker een factory gebruiken.

Acties:
  • 0 Henk 'm!

Verwijderd

een truuk die ik in php4 uithaal is een combinatie van de $GLOBALS array met een constante en een uniqid();.

werkt als volgt, stel we hebben 1 class, errorlogger bijvoorbeeld. dan maak in mijn header die ik ook
gebruik om alle alle code etc te includen en andere constanten te zetten. voeg ik dit aan toe.
PHP:
1
2
3
 if(!defined("MYPROJECT_ERRORLOGGER_ID")){
  define("MYPROJECT_ERRORLOGGER_ID", uniqid("MYPROJECT_ERRORLOGGER_ID_"), true);
 }


dit defineert de constante MYPROJECT_ERRORLOGGER_ID en geeft deze een random waardie in de trant van MYPROJECT_ERRORLOGGER_ID_8238272911. ik gebruik bewust een random id met een ruime prefix zodat ik de kans dat ik andere ge-includde code in de weg zit minimaal is.

vervolgens maak ik in de constructor van elke class welke gebruikt wil maken van de errorlogger singleton de volgende constructie.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class dbal
{
  var $_errorlogger;

  function dbal()  {
   $this->__construct();
  }

  function __construct() {
   $this->_errorlogger = false;

   if(defined("MYPROJECT_ERRORLOGGER_ID")){
     if(isset($GLOBALS[MYPROJECT_ERRORLOGGER_ID])){
      $this->_errorlogger =& $GLOBALS[MYPROJECT_ERRORLOGGER_ID];
     }else{
      $this->_errorlogger = new errorlogger();
      $GLOBALS[MYPROJECT_ERRORLOGGER_ID] =& $this->_errorlogger;
     }
   }else{
    trigger_error("CANNOT Connect to ERRORLOGGER as its constant is not set.", E_USER_ERROR);
   }
 }
}


Op het moment dat ik de class 'dbal' aanmaak, kijk de constructor of de constante bestaat zo niet dan is de code niet goed ge-include. vervolgens kijkt deze of dat de errorlogger al is aangemaakt in de GLOBALS array, zo ja dan pakt deze een reference hiervan, zoniet dan maakt de class de errorlogger aan en voegt een reference toe aan de GLOBALS array zodat andere classess die zelfde reference weer opakken. en omdat een constante dus maar 1 keer kan bestaan en onwijzigbaar is onstaat er dus ook maar 1 instantie van errorlogger en de rest zijn references naar die ene.

let ook op hoe ik de php4 constructor doorverwijz naar de php5 constructor zodat mijn code zowel voor php4 als php5 werkt en ik maar 1 constructor hoef te onderhouden. alhoewel php5 zelf ook naar
de php4 constructor zoekt vindt ik dit toch een nettere manier. ook wanneer php5 hiermee stopt.

elke class pakt een reference naar de errorlogger en vindt deze middels zijn constante. door een willekeurig id te kiezen als waarde voor de constante blijft de vervuiling van de GLOBALS array minimaal.

Acties:
  • 0 Henk 'm!

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
Verwijderd schreef op donderdag 19 april 2007 @ 23:36:
een truuk die ik in php4 uithaal is een combinatie van de $GLOBALS array met een constante en een uniqid();.

werkt als volgt, stel we hebben 1 class, errorlogger bijvoorbeeld. dan maak in mijn header die ik ook
gebruik om alle alle code etc te includen en andere constanten te zetten. voeg ik dit aan toe.
PHP:
1
2
3
 if(!defined("MYPROJECT_ERRORLOGGER_ID")){
  define("MYPROJECT_ERRORLOGGER_ID", uniqid("MYPROJECT_ERRORLOGGER_ID_"), true);
 }


dit defineert de constante MYPROJECT_ERRORLOGGER_ID en geeft deze een random waardie in de trant van MYPROJECT_ERRORLOGGER_ID_8238272911. ik gebruik bewust een random id met een ruime prefix zodat ik de kans dat ik andere ge-includde code in de weg zit minimaal is.

vervolgens maak ik in de constructor van elke class welke gebruikt wil maken van de errorlogger singleton de volgende constructie.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class dbal
{
  var $_errorlogger;

  function dbal()  {
   $this->__construct();
  }

  function __construct() {
   $this->_errorlogger = false;

   if(defined("MYPROJECT_ERRORLOGGER_ID")){
     if(isset($GLOBALS[MYPROJECT_ERRORLOGGER_ID])){
      $this->_errorlogger =& $GLOBALS[MYPROJECT_ERRORLOGGER_ID];
     }else{
      $this->_errorlogger = new errorlogger();
      $GLOBALS[MYPROJECT_ERRORLOGGER_ID] =& $this->_errorlogger;
     }
   }else{
    trigger_error("CANNOT Connect to ERRORLOGGER as its constant is not set.", E_USER_ERROR);
   }
 }
}


Op het moment dat ik de class 'dbal' aanmaak, kijk de constructor of de constante bestaat zo niet dan is de code niet goed ge-include. vervolgens kijkt deze of dat de errorlogger al is aangemaakt in de GLOBALS array, zo ja dan pakt deze een reference hiervan, zoniet dan maakt de class de errorlogger aan en voegt een reference toe aan de GLOBALS array zodat andere classess die zelfde reference weer opakken. en omdat een constante dus maar 1 keer kan bestaan en onwijzigbaar is onstaat er dus ook maar 1 instantie van errorlogger en de rest zijn references naar die ene.

let ook op hoe ik de php4 constructor doorverwijz naar de php5 constructor zodat mijn code zowel voor php4 als php5 werkt en ik maar 1 constructor hoef te onderhouden. alhoewel php5 zelf ook naar
de php4 constructor zoekt vindt ik dit toch een nettere manier. ook wanneer php5 hiermee stopt.

elke class pakt een reference naar de errorlogger en vindt deze middels zijn constante. door een willekeurig id te kiezen als waarde voor de constante blijft de vervuiling van de GLOBALS array minimaal.
Dit is inderdaad een truuk, of ook een hack. Ik geloof absoluut dat het werkt, maar je lost hiermee het probleem niet op: je moet nu voor iedere klasse deze code kopiëren (en code kopiëren is per definitie slecht).

Acties:
  • 0 Henk 'm!

  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

robbert schreef op donderdag 19 april 2007 @ 18:21:
[...]

Zo kun je ook direct zeggen dat er helemaal geen spake is van OOP in php4. Je kan stiekem toch gewoon de interne structuur van de klasse verprutsen.
Niet lezen wat je wil lezen. Het feit dat PHP4 gewoon geen accesibility modifiers ondersteund en met name voor de constructor is wel degelijk iets om rekening mee te houden als je singleton pattern wil implementeren. Zonder dit is er gewoon simpelweg geen garantie dat er ten alle tijden maar sprake is van 1 instantie van een zeker object; hetgeen dat het hele idee is achter singleton pattern. Het is dan een keuze om deze pattern te gebruiken, zelf ben ik van mening dat je prima zonder kan als je gewoon zelf als programmeur zijnde hier rekening mee houdt.

Er is echter volgens mij wel een mogelijkheid om 1 instantie per klasse af te dwingen in PHP4, door de $this reference te overschrijven binnen de constructor. Zoiets als function Constructor() { $t= "this"; $$t = &$jesingleton; }. Lexicaal voorkomt php dat je een assignment doet op $this, maar je kan hier eventueel omheen gaan via deze wijze en de this pointer over schrijven. Ik hoef hoop ik niet erbij te zeggen dat dit een belachelijk iets is? ;) Gelukkig werkt het dan volgens mij ook niet meer in nieuwere versies van PHP.

Bjarne Stroustrup definieert overigens een object georienteerde taal als een taal die mechanismen levert die programmeren volgens het object georienteerde paradigm MAKKELIJKER maakt. Het bakken van hacks voor singleton, overloaden etc... om dingen die eigenlijk als mechanismen horen vind ik persoonlijk zelf niet echt vallen onder de definitie van makkelijker maken, en PHP voldoet dan voor mij amper aan de definitie van een OO taal.
[...]

Je kan die methode gewoon als static zijnde gebruiken, het static keyword bestond nog niet in php4.
En ook dat is mijn punt.

Verder is zoiets als loggen een schoolvoorbeeld van een crosscutting concern (er is namelijk sprake van scattered en tangled logger code in je business logics) en zou je eventueel willen overwegen om op te willen lossen middels aspect orientatie. Dat hoeft niet eens persee via een aparte weaver, aangezien je met de reflection API opzich ook before en after advice kan superimposen op de bestaande code. Je moet dan denken aan een proxyfactory die gegeven een instantie van waar je op wil loggen, deze instantie gewrapped teruggeeft. De wrapper dient dan covariant te zijn met respect tot de (declaritieve) type van het object dat gegeven is om te wrappen om abstractie te handhaven.

Je moet dan denken aan zoiets als:

PHP:
1
2
3
4
5
$db = new DbConnection(); // en we veronderstellen even dat deze een select() methode heeft.
$logger = new Logger(); //en dan eigenlijk Logger implements BeforeMethodAdvice { function before(); }
ProxyFactory::setBeforeMethodAdvice($logger); //Geef advies om beforeadvice te superimposen aan de methoden van instanties de gemaakt worden door deze proxy factory.
$dbProxy = ProxyFactory::create($db);//$dbProxy instanceof DbConnection moet gelden voor abstractie, i.e. waar DbConnection verwacht wordt kan $dbProxy ook meegegeven worden omdat deze specifieker is.
$dbProxy->select(); //Nu wordt dus eerst de logger (immers BeforeMethodAdvice) aangeroepen voordat $db->select() wordt aangeroepen (delegatie).


Hou er echter wel rekening mee dat AOP eventueel obliviousness met zich mee kan brengen, omdat er code gesuperimposed wordt tijdens runtime op deze wijze, waardoor het niet snel duidelijk kan zijn waar het fout gaat als het fout gaat ;). Een goed begin is in ieder geval om PHP5 te overwegen als dat tot de opties behoort.

[ Voor 33% gewijzigd door prototype op 20-04-2007 02:58 ]


Acties:
  • 0 Henk 'm!

Verwijderd

chris schreef op vrijdag 20 april 2007 @ 02:13:
[...]


Dit is inderdaad een truuk, of ook een hack. Ik geloof absoluut dat het werkt, maar je lost hiermee het probleem niet op: je moet nu voor iedere klasse deze code kopiëren (en code kopiëren is per definitie slecht).
het is inderdaad een hack maar veel dichter tot een singleton-achtig iets kom je niet in PHP4.

je kunt niet met 100% zekerheid zeggen dat er geen 2e instantie van errorlogger zou kunnen onstaan maar als alle classen die errorlogger gebruiken dit in hun constructor hebben staan kun je wel met 100% zekerheid zeggen dat deze een-en-dezelfde instantie van errorlogger gebruiker: een singleton dus :)

en dat je dit in naar elke class zou moeten kopieeren is ook geen ramp want PHP4 ondersteunt gelukkig wel 'extends' dus je zou een base class kunnen maken, daar deze code in de constructor zetten en alle andere classes die dan op deze base class extenden krijgen dan automatische een reference naar die ene errorlogger.

zolang al die andere classen met hun poten van de $GLOBALS array en reference in $this->_errorlogger afblijven is er niets aan de hand, maar als iemand de $GLOBALS array zou leeg gooien dan verdwijnt dus ook de reference en maakt de volgende constructor een nieuwe instantie aan in de $GLOBALS array welke de daaropvolgende constructor's weer zullen gebruiken waardoor de nog bestande instanties met de oude errorlogger werken terwijl de nieuwere met de nieuw aangemaakte errorlogger werken:
2 instantie's van errorlogger en dus geen singleton meer :'(

een beter hack voor singleton-achtige OOP in PHP4 heb ik nog niet ontdenkt maar de GLOBALS array is van ons allemaal, dat 1 stuk code die compleet zou mogen toe-eisen is ook een denk fout van die ene programmeur, en destructief gewoon leeggooien nog erger. zulk soort code wil ik niet eens aan mijn project toevoegen maar als je dat kunt voorkomen heb je hier een mooie singleton oplossing voor PHP4.
Pagina: 1