Objects instantieren met data al in private properties

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Pagina: « vorige 1 2

25/05/2013 13:52:17
Quote Anchor link
@Erwin
Je hebt nu twee mogelijkheden, oftewel roep je de constructor helemaal niet aan... wat ook voor problemen kan zorgen of je roept de constructor wel aan met de argumenten die je zelf toevoegt.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?php

class Instance {
    
    /**
     * Create a new instance of the object and add some properties even
     * when they are private or protected.
     *
     * @param string $class,
     * @param array $properties,
     * @param array | bool $constructor,
     * @return bool | object
     */

    public static function create($class, array $properties = array(), $constructor = false) {
    
        /**
         * Check if the class exists. When the class could not been loaded
         * return false.
         */

        if(class_exists($class, true) === false) {
            return false;
        }

        
        /**
         * Go on. Make the class and add all the properties the user gave
         * in the array.
         */

        $reflection = new ReflectionClass($class);
        
        if($constructor === false) {
            $instance = $reflection->newInstanceWithoutConstructor();    
        }

        
        else {
            $instance = $reflection->newInstanceArgs((array) $constructor);
        }

        
        foreach($properties as $property => $value) {
            
            try {
            
                /**
                 * Get the property from the $reflection, make it accessible and
                 * set the value.
                 */

                $reflectionProperty = $reflection->getProperty($property);
                $reflectionProperty->setAccessible(true);
                $reflectionProperty->setValue($instance, $value);
                
            }

            
            catch(ReflectionException $e) {
            
                /**
                 * Handle the exception or do nothing.
                 */

                
            }
            
        }

        
        return $instance;
        
    }
        
}


class User {

    /**
     * The identifier for the user.
     *
     * @param int $userId
     */

    private $userId            = 'ForNowAString';
    
    /**
     * For test purposes only.
     */

    private $constructor         = false;
    
    public function __construct() {
        $this->constructor = 'YouHaveCalledTheConstructor';
    }

    
    /**
     * Add the user id to this class.
     *
     * @param int $userId,
     * @return void
     */

    public function setUserId($userId) {
        $this->userId = $userId;
    }

    
    /**
     * Return the identifier for the user.
     *
     * @return int $userId
     */

    public function getUserId() {
        return (int) $this->userId;
    }

}


print_r( Instance::create('User', array('userId' => 1), true) );

print_r( Instance::create('User', array('userId' => 1)) );

?>
Gewijzigd op 25/05/2013 14:11:26 door
 
PHP hulp

PHP hulp

18/11/2024 03:29:12
 
Erwin H

Erwin H

25/05/2013 14:24:03
Quote Anchor link
Dat met de constructor aanroepen is geen optie. Zie de reden waarom we hier uberhaupt over praten.

En je optie om het aan te roepen zonder constructor is ook geen optie, want dan krijg ik:
Fatal error: Call to undefined method ReflectionClass::newInstanceWithoutConstructor()
(ik heb php 5.3.4 en die method is er pas vanaf php 5.4.0)
 

25/05/2013 14:49:03
Quote Anchor link
De code die je op pagina één gaf heb ik hier niet getest, maar is het ook mogelijk als bijvoorbeeld de klasse Admin de klasse User aanvult om die parameters mee te geven?

Want als je er serialize() op loslaat krijg je de volgende code gepresenteerd.
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
O:5:"Admin":3:{s:12:"*signature";N;s:12:"UseruserId";s:13:"ForNowAString";s:17:"Userconstructor";s:27:"YouHaveCalledTheConstructor";}


Daarnaast is er met mijn code de mogelijkheid om de constructor aan te roepen. Niemand verplicht je hier natuurlijk toe. Maar het kan wel voordelen hebben als je bijvoorbeeld een object aanmaakt in de constructor die nodig is om de klasse te laten werken of om toch data door te spelen.

Dat instanceWithoutConstructor() vanaf versie 5.4 aanwezig is is dus natuurlijk wel een probleem. Maar meestal gebruik ik één van de laatste versies van PHP en dus kreeg ik geen errors.
Gewijzigd op 25/05/2013 14:57:03 door
 
Erwin H

Erwin H

25/05/2013 15:26:18
Quote Anchor link
Aaron, waar het om ging was hoe PDO het voor elkaar krijgt om willekeurig welk object te vullen met willekeurig welke private properties. Dat is wat ik heb proberen te simuleren met de code in de eerste post. Het doel hiervan is om de PDO functionaliteit te kunnen copieren naar een database class op het moment dat je een andere database interface dan PDO gebruikt die niet de optie FETCH_CLASS ondersteunt.

Je geeft dan een classnaam mee, de database class trekt de data uit de database en stopt dat in een nieuw aan te maken object (van de class die je hebt meegegeven). Het is daarbij dus niet van belang welke class daarbij wordt ge-extend. Een constructor wil je dus ook juist niet aanroepen, want de generieke database class moet zich geen zorgen gaan maken over welke parameters dan allemaal mee moeten. Dit houdt overigens wel in dat je geen classnaam mee zou moeten geven van een class die afhankelijk is van andere objecten die in de constructor meegegeven moeten worden.

Overigens betekent dit laatste dus ook dat de oplossing die ik eerder moest toepassen om jouw methode werkend te krijgen eigenlijk best acceptabel is. In alle gevallen mag de class niet afhankelijk zijn van de constructor, anders werkt het niet meer generiek genoeg.
 

25/05/2013 15:40:45
Quote Anchor link
Ik gebruik altijd FETCH_INTO & FETCH_CLASS dus ik weet waarover het onderwerp ging. Alleen snap ik niet waarom de constructor niet aangeroepen mocht worden, omdat dat dus wel gebeurt als je FETCH_INTO & FETCH_CLASS gebruikt.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?php

$sth
->fetchAll(PDO::FETCH_CLASS, 'User');
$sth->fetchAll(PDO::FETCH_INTO, new User());

?>

In het eerste geval kun je niet zien dat de constructor word aangeroepen... maar dit is dus wel het geval en als je gewoon een gebruiker aanmaakt roep je sowieso de constructor aan.

Een klasse mag niet afhankelijk zijn van de constructor. Maar het kan dus wel nuttig zijn om toch de variabelen door te geven aan de constructor als je dat wenst en dat is dus mogelijk maar hoeft niet.
Gewijzigd op 25/05/2013 15:44:17 door
 
Ozzie PHP

Ozzie PHP

25/05/2013 16:03:28
Quote Anchor link
Aaron, het gaat er eigenlijk om of je PDO::FETCH_CLASS moet gebruiken. Het lijkt mij een heel handige methode, alleen niet alle database interfaces ondersteunen het. Op dit moment is dat helemaal niet erg, want ik ben niet van plan om over te stappen op een andere interface.

Echter, en daar gaat dit hele verhaal over, stel dat ik over 10 jaar om wat voor reden dan ook wel ineens zou willen/moeten(?) overstappen op een andere database interface die de fetch_class methode niet ondersteunt, dan werkt de code dus niet meer.

Grofweg kan ik nu 2 dingen doen. Ik gebruik gewoon de PDO::FETCH_CLASS methode en mocht ik over een aantal jaren overstappen naar een andere databse interface dan zal ik op dat moment een andere oplossing moeten vinden (bijv. de oplossing van Erwin of die van jou). Maar voor nu gebruik ik dan gewoon FETCH_CLASS en later, ach.. wie dan leeft wie dan zorgt. Of, optie 2, i gebruik die hele FETCH_CLASS methode niet maar ik gebruik direct al de methode van Erwin of jou.

Optie 1 heeft als voordeel dat ik nu niet moeilijk hoef te doen en gewoon PDO::FETCH_CLASS kan gebruiken. Echter, heel misschien betekent dit dat ik ooit in de toekomst als ik overstap naar een andere database interface ik een en ander zou moeten aanpassen.

Optie 2 heeft als voordeel dat ik nooit meer iets hoef aan te passen, maar het nadeel is dat het omslachtiger is (meer code voor hetzelfde resultaat) en dat het significant trager werkt dan optie 1, wat dus nadelig is voor de performance.

Wat is jouw visie hierop?
 

25/05/2013 16:11:20
Quote Anchor link
Stel nu dat PDO komt te vervallen. Dan zul je sowieso de hele applicatie (of in mijn geval de Mapper klassen) moeten herschrijven. Dus opzich lijkt dat voor mij geen probleem! Voor mysql_* is er ook een fetch object, dus de volgende database interface zal dit waarschijnlijk ook ondersteunen.

Ook zou je gebruik kunnen maken van een wrapper die dan de verschillende database interfaces ondersteunt en dan kun je heel gemakkelijk overschakelen van de één naar de ander. Neem nu aan dat de volgende interface geen fetch object heeft kun je de bovenstaande code in no-time implementeren en dan is het probleem opgelost!

De bovenstaande code zou je wel kunnen gebruiken in een File klasse die dus bestanden inleest. Als je dan een object terug wilt heb je de bovenstaande code dus nodig. Anders zou ik er niet voor gaan.
Gewijzigd op 25/05/2013 16:15:52 door
 
Ozzie PHP

Ozzie PHP

25/05/2013 16:15:07
Quote Anchor link
Thanks voor je reactie Aaron.

"De bovenstaande code zou je wel kunnen gebruiken in een File klasse die dus bestanden inleest. Als je dan een object terug wilt heb je de bovenstaande code dus nodig. Anders zou ik er niet voor gaan."

Wat bedoel je hiermee? Kun je een voorbeeldje geven?

"Stel nu dat PDO komt te vervallen. Dan zul je sowieso de hele applicatie (of in mijn geval de Mapper klassen) moeten herschrijven."

het zou dan juist mooi zijn als je bijv. alleen de connectie met de database hoeft te wijzigen en dat de rest gewoon blijft werken :)

Maar je hebt wel een punt. Misschien is het zinvoller om voor nu gewoon PDO::FETCH_CLASS te gebruiken en pas op het moment dat het aan de orde is een alternatief te gaan maken. (Hopelijk kan ik dit topic dan nog terugvinden...)
 

25/05/2013 16:18:47
Quote Anchor link
Sommige mensen hebben een CMS die draait op bestanden of delen ervan die bestanden gebruiken. Neem nu aan dat dit zo is, dan is het gemakkelijk om de bestanden uit te lezen en door te geven naar Instance::create().

Je zou natuurlijk gewoon nog altijd de setMethod() & getMethod() kunnen gebruiken. Maar daarvoor moest er geen topic aangemaakt worden.

Als je dan toch zou willen veranderen van interface maak je waarschijnlijk beter gebruik van wrappers zodat je in no-time kunt veranderen. Als er dan een nieuwe database interface komt maakt je een nieuwe wrapper en dan is het opgelost!

Maar zeg nu zelf, om de hoeveel tijd zal PHP de database interface drastisch veranderen?
Gewijzigd op 25/05/2013 16:25:59 door
 
Erwin H

Erwin H

25/05/2013 16:20:25
Quote Anchor link
Net even getest en inderdaad, als je FETCH_CLASS gebruikt wordt wel de constructor aangeroepen. En, die geeft dus ook een foutmelding als er een parameter ontbreekt.

Met het laatste ben ik het overigens totaal niet eens. Een class kan juist wel afhankelijk zijn van de constructor, sterker, het is in veel gevallen volkomen juist om dat te doen. Op die manier leg je namelijk strikte afhankelijkheden tussen classes (wanneer dat nodig is). Het is juist een goed uitgangspunt om ervoor te zorgen dat als een benodigd object ontbreekt je een compile time error krijgt en niet een run time error. Dat forceer je door dit soort objecten mee te geven in de constructor en niet via setters.
(disclaimer: compile time errors in php is een beetje een onjuiste term omdat het compileren run time gebeurd, maar ik hoop dat evengoed duidelijk is wat ik bedoel)

Maar samenvattend:
- welke manier je ook gebruikt, de constructor zou zonder parameters aangeroepen moeten worden, anders krijg je bijna altijd ofwel foutmeldingen, ofwel problemen
- er zijn meerdere manieren om het te doen
- qua performance is het langzamer dan direct instantieren (lijkt me een open deur), maar de twee methodes zijn wat mij betreft niet significant verschillend


Edit: even de quote erbij omdat er inmiddels wat posts tussen waren gekomen
Aaron - op 25/05/2013 15:40:45:
Een klasse mag niet afhankelijk zijn van de constructor. Maar het kan dus wel nuttig zijn om toch de variabelen door te geven aan de constructor als je dat wenst en dat is dus mogelijk maar hoeft niet.
Gewijzigd op 25/05/2013 16:21:38 door Erwin H
 

25/05/2013 16:25:39
Quote Anchor link
@Erwin
Een klasse kan dus afhankelijk zijn. Maar de meeste klassen die FETCH_CLASS gebruiken zijn helemaal nergens afhankelijk. Want de meeste zijn gewoon setMethod() & getMethod() bij mij. Voor de rest gebruik ik mijn Mappers waar de constructor dus wel belangrijk is. Daarom is er dus ook bij mijn oplossing de mogelijkheid om de parameters door te geven aan de constructor, zo wordt die toch aangeroepen.

Het kan altijd nuttig zijn om toch de constructor aan te roepen, want anders had PHP de __construct() & __destruct() functies niet geïmplementeerd.
Gewijzigd op 25/05/2013 16:27:10 door
 
Ozzie PHP

Ozzie PHP

25/05/2013 16:26:21
Quote Anchor link
"Net even getest en inderdaad, als je FETCH_CLASS gebruikt wordt wel de constructor aangeroepen. En, die geeft dus ook een foutmelding als er een parameter ontbreekt."

Maar hoe geef je dan met FETCH_CLASS een argument mee aan de constructor?
 

25/05/2013 16:28:35
Quote Anchor link
Dat kan niet Ozzie. Of toch niet dat ik weet. Een simpele oplossing is gewoon om __construct() niet te gebruiken of om __construct($userId = false) of dergelijke te implementeren. Dus een workaround. In deze gevallen zou je dan wel FETCH_INTO kunnen gebruiken waar je wel parameters kunt doorsturen naar de klasse, maar nog altijd geen van de rijen die je terug gekregen hebt.

-----

Het kan dus wel. Maar je kunt nog altijd geen argumenten van de rij zelf meegeven? Zie: http://stackoverflow.com/questions/9127693/pdo-using-pdofetch-props-late-and-construct-call#answer-9134544
Gewijzigd op 25/05/2013 16:42:37 door
 
Ozzie PHP

Ozzie PHP

25/05/2013 16:32:46
Quote Anchor link
Ah oke... dus of géén constructor, of een constructor met een default waarde. Ik snap 'm.

Ik denk dat ik in m'n PDO class gewoon een fetchClass method maak, die dan gebruikmaakt van PDO::FETCH_CLASS en als ik dan ooit (met de nadruk op ALS en OOIT) zou moeten overstappen naar een andere interface, dan maak ik voor die interface wel een nieuwe fetchClass method.

Bedankt allebei voor jullie reacties. Het was weer een interessant topic :-)

Toevoeging op 25/05/2013 16:33:41:

Aaron - op 25/05/2013 16:28:35:
Het kan dus wel. Maar je kunt nog altijd geen argumenten van de rij zelf meegeven? Zie: http://stackoverflow.com/questions/9127693/pdo-using-pdofetch-props-late-and-construct-call#answer-9134544

Oké!
 

Pagina: « vorige 1 2



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.