Toon posts:

[PHP] Array mapper

Pagina: 1
Acties:

  • g4wx3
  • Registratie: April 2007
  • Laatst online: 24-05 13:08
De associative array is de belangrijkste datastructuur in PHP. Standaard zijn er zo al een paar geïnitieerd, $_GET, $_POST,$_SESSION, ..php_superglobals

Maar array's zijn nogal... foutgevoelig. Voor eigen datastructuren kan je uitwijken naar objecten (zoals stdClass, ArrayObject) Maar standaard kan je ook hier nog steeds keys toevoegen of overschrijven zonder weten. Dit kan je enkel tegengaan door volledige dataclassen maken waarin je dergelijke regels kan opleggen. Dat is wel omslachtig, en je mist anderzijds veel handige array-functies. (tenzij je telkens cast)

In onderstaande klasse heb ik het idee uitgewerkt om een array te mappen "by reference" naar een nieuwe object.
De keys kan ik op voorhand makkelijk definieren. Hierdoor kan ik de bestaande array blijven gebruiken, terwijl ik op het object op elegante wijze value's kan getten, of setten, met fout controle.
In bestaande frameworks heb ik dergelijke oplossing nog niet voorbij zien komen.

Is het een goed idee? Zouden jullie dingen anders doen?

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
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
112
113
<?php

// generic data_referenceClass
class ErrorType
{
    // bits *000(0)
    const none = 0;
    // bits *001(1)
    const on_create = 1;
    // bits *010(2)
    const on_overwrite = 2;
    // bits: *111(7) (because bits *011(3) is confusing)
    const all = 7;
}
class ArrayMapper
{
    static function test(){
        // datastructure as array
        $data_array = array();
        // map array to object with arrayMapper
        $data_mapper = new ArrayMapper($data_array, ErrorType::on_create);
        
        // dataMirror is coherrent with reference
        $data_array['greet'] = 'hello world';
        echo $data_mapper->greet;

        // reference is coherent with ArrayMirror
        $data_mapper->greet = 'hello php';
        echo $data_array['greet'];

        // if somehow you lost access to the original array, get new reference
        $data_array2 = &$data_mapper->getArray();
        
        // the old one will keep up to date
        $data_array2['salute'] = 'New salute';
        echo $data_array['salute'];
        
        // other solutions see eg arrayObject
        //$arrayObject = new ArrayObject();
        //$arrayObject->setFlags(ArrayObject::STD_PROP_LIST|ArrayObject::ARRAY_AS_PROPS);

    }

    // static $sdata_reference;
    private $data_reference;
    private $error_type;

    // bind data_refernce on init
    public function __construct(&$data_reference,$error_type=ErrorType::none){
        if(!is_array($data_reference))
            throw new \exception('Data_reference is not an array');
        $this->data_reference = &$data_reference;
        $this->setErrorType($error_type);
    }
    // error_type
    public function setErrorType($error_type){
        $this->error_type = $error_type;
    }

    // Magic set
    public function __set($property, $value) {
        $this->set($property, $value,$this->error_type);
    }
    
    // Magic get
    public function __get($property) {
        return $this->get($property);
    }

    // Has key
    public function has($key){
        return isset($this->data_reference[$key]);
    }
    // get new reference
    public function &getArray(){
        return $this->data_reference;
    }
    // data_reference setter
    public function set($key=NULL,$value=NULL,$error_type=NULL){
        if(is_null($error_type))
            $error_type = $this->error_type;    
        // binary compare '111'(7) with error_type: 1 & 0 = 0 only 1 & 1 = 1
        if(!isset($this->data_reference[$key]) && 0<(ErrorType::on_create & $error_type) )
            throw new Exception(sprintf('Key already exist: %s old value: %s',$key,$this->data_reference[$key] ));
        if(isset($this->data_reference[$key]) && 0<(ErrorType::on_overwrite & $error_type) )
            throw new Exception(sprintf('Key does not exist',$key,$this->data_reference[$key] ));

        $this->data_reference[$key] = $value;
        //get value: $value = $object->set('key','value',false);
        return $value;
    }

    // data_reference getter
    public function get($key=null,$default_value=null,$Exception_on_null=NULL){
        if( is_null($key) )
             throw new Exception('Key is invalid (NULL)');
            
        // set $Exception_on_null if defaultvalue is null
        if( !isset($this->data_reference[$key])){
            if(  is_null($default_value) && (is_null($Exception_on_null) || $Exception_on_null == true)){
                throw new Exception('Key does not exist: '.$key);
            }
        }
        else{
            $default_value = $this->data_reference[$key];
        }
        return $default_value;
    }
}



ArrayMapper::test();

[Voor 13% gewijzigd door g4wx3 op 10-10-2018 20:20]

http://www.softfocus.be/


  • Sithistar
  • Registratie: November 2005
  • Niet online
Ik begrijp werkelijk niet welk probleem je hier denkt op te lossen, dat ook niet met een simpele functie kan.

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 23:13

CurlyMo

www.pilight.org

g4wx3 schreef op woensdag 10 oktober 2018 @ 18:12:
Ze zijn omslachtig om te definiëren.
Dus voeg je meer complexiteit toe?
Een spelfout kan leiden tot een extra item, en je eigenlijke item blijft hetzelfde.
Het zou mooi zijn als een programmeertaal voor je gaat nadenken. Dat PHP voor je beslist dat je niet Huis bedoelde maar Muis. Dat bedoel je denk ik niet. Maar wat je volgens mij wil voorkomen is niet waarvoor een array is bedoeld. Daarvoor kan je beter direct naar pure objecten gaan met private members die je alleen via getters en setters kan beïnvloeden. Het mooie juist aan arrays is de flexibiliteit ervan.
Of je overschrijft onbewust een bestaan item.
Nee, want het is aan jouw als programmeur om aan controles te doen. Of je die logica nu in een klasse zet of er buiten.
Ik gebruik deze classe niet in productie, maar is het en goed idee?
Ik snap het probleem niet dat je wil oplossen? Het klinkt als een soort ORM waarbij je data-laag je array's zijn?

As je naar een soort getter/setter achtige objecten wil, maak ze dan functioneel zinvol i.p.v. puur een array omzetten naar een object. Ook denk ik dat je alles alleen maar ondoorzichtiger maakt door een reference te houden naar de oorspronkelijk array's.

Wat je wel ziet zijn helpende functies zoals getopt die het makkelijker maakt om de $argv global te verwerken. Maar zulke functies zijn in hun scope afgebakend en klein gehouden.

geen vragen via PM die ook op het forum gesteld kunnen worden.


  • Solopher
  • Registratie: December 2002
  • Laatst online: 30-05 13:36
Ik zou het inderdaad anders doen:

Je classes final maken, en alleen via de constructor alleen laten vullen. Oftewel gebruik maken van Value Objects.

Een artikel hierover:
https://dzone.com/article...ctical-php-patterns-value

Maar ik zou dan wel PHP 7+ gebruiken:

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
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
<?php
declare(strict_types=1);

namespace Picasso; 

/**
 * Subclassing a Value Object is rare, so we cut out this scenario
 * immediately.
 * This class represent a category of paint, like 'blue' or 'red',
 * not a quantity.
 */
final class Paint
{
    private $red;
    private $green;
    private $blue;

    /**
     * This is paint for screens... No CMYK.
     * @param int   0-255
     * @param int   0-255
     * @param int   0-255
     */
    public function __construct(int $red, int $green, int $blue)
    {
        $this->red = $red;
        $this->green = $green;
        $this->blue = $blue;
    }

    /**
     * Getters expose the field of the Value Object we want to leave
     * accessible (often all).
     * There are no setters: once built, the Value Object is immutable.
     * @return integer
     */
    public function getRed(): int
    {
        return $red;
    }

    public function getGreen(): int
    {
        return $green;
    }

    public function getBlue(): int
    {
        return $blue;
    }

    /**
     * Actually, confronting two objects with == would suffice.
     * @return boolean  true if the two Paints are equal
     */
    public function equals($object): bool
    {
        return get_class($object) == 'Paint'
           && $this->red == $object->red
           && $this->green == $object->green
           && $this->blue == $object->blue;
    }

    /**
     * Every kind of algorithm, just to expanding this example.
     * Since the objects are immutable, the resulting Paint is a brand 
     * new object, which is returned.
     * @return Paint
     */
    public function mix(Paint $another): Paint
    {
        return new Paint($this->integerAverage($this->red, $another->red),
                         $this->integerAverage($this->green, $another->green),
                         $this->integerAverage($this->blue, $another->blue));
    }

    private function integerAverage(int $a, int $b): int
    {
        return (int) (($a + $b) / 2);
    }
}

// client code
$blue = new Paint(0, 0, 255);
$red = new Paint(255, 0, 0);
var_dump($blue->equals($red));
var_dump($violet = $blue->mix($red));


Bovenstaand is pseudo code heb hier geen php parser beschikbaar.

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 03-06 11:46

Janoz

Moderator Devschuur®

!litemod

@Solopher Je bedoelt Immutable Objects. Een value object is niks meer dan een object waarvoor geld dat twee verschillende instanties gelijk aan elkaar zijn wanneer hun inhoud hetzelfde zijn, ondanks dat het andere instantie zijn.

En mbt het voorstel van @g4wx3, het probleem dat hij denkt op te lossen kan veel simpeler, duidelijker en robuuster opgelost worden door zelf objecten te maken, een fatsoenlijke IDE te gebruiken en unittests te schrijven.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Solopher
  • Registratie: December 2002
  • Laatst online: 30-05 13:36
Janoz schreef op vrijdag 12 oktober 2018 @ 09:00:
@Solopher Je bedoelt Immutable Objects. Een value object is niks meer dan een object waarvoor geld dat twee verschillende instanties gelijk aan elkaar zijn wanneer hun inhoud hetzelfde zijn, ondanks dat het andere instantie zijn.
Je hebt helemaal gelijk! Het was nogal een lange dag gisteren :P
Pagina: 1


Tweakers maakt gebruik van cookies

Tweakers plaatst functionele en analytische cookies voor het functioneren van de website en het verbeteren van de website-ervaring. Deze cookies zijn noodzakelijk. Om op Tweakers relevantere advertenties te tonen en om ingesloten content van derden te tonen (bijvoorbeeld video's), vragen we je toestemming. Via ingesloten content kunnen derde partijen diensten leveren en verbeteren, bezoekersstatistieken bijhouden, gepersonaliseerde content tonen, gerichte advertenties tonen en gebruikersprofielen opbouwen. Hiervoor worden apparaatgegevens, IP-adres, geolocatie en surfgedrag vastgelegd.

Meer informatie vind je in ons cookiebeleid.

Sluiten

Toestemming beheren

Hieronder kun je per doeleinde of partij toestemming geven of intrekken. Meer informatie vind je in ons cookiebeleid.

Functioneel en analytisch

Deze cookies zijn noodzakelijk voor het functioneren van de website en het verbeteren van de website-ervaring. Klik op het informatie-icoon voor meer informatie. Meer details

janee

    Relevantere advertenties

    Dit beperkt het aantal keer dat dezelfde advertentie getoond wordt (frequency capping) en maakt het mogelijk om binnen Tweakers contextuele advertenties te tonen op basis van pagina's die je hebt bezocht. Meer details

    Tweakers genereert een willekeurige unieke code als identifier. Deze data wordt niet gedeeld met adverteerders of andere derde partijen en je kunt niet buiten Tweakers gevolgd worden. Indien je bent ingelogd, wordt deze identifier gekoppeld aan je account. Indien je niet bent ingelogd, wordt deze identifier gekoppeld aan je sessie die maximaal 4 maanden actief blijft. Je kunt deze toestemming te allen tijde intrekken.

    Ingesloten content van derden

    Deze cookies kunnen door derde partijen geplaatst worden via ingesloten content. Klik op het informatie-icoon voor meer informatie over de verwerkingsdoeleinden. Meer details

    janee