Autor Zpráva
quatzael
Profil
Chci nějak vyřešit tohle co je v ukázce a samozřejmě nefunguje. Jde to prosím nějak?

$array['aaa']['bbb']['ccc'] = "value";
$subarray = "['bbb']['ccc']";
echo $array['aaa']$subarray;  //má zobrazit to samé co $array['aaa']['bbb']['ccc'] tedy "value"
Alphard
Profil
Jedině si naimplementovat nějakou funkci, která přejde na další úrovně zanoření
echo getByKeys($array['aaa'], ['bbb', 'ccc']);
quatzael
Profil
A takhle by to nějak nešlo?

$array["aaa"]["bbb"]["ccc"] = "value";
$string = 'echo $array["aaa"]["bbb"]["ccc"];';
 parse_str($string);

Tohle teda taky nefunguje, ale moc nechápu proč..
Joker
Profil
quatzael:
On je podezřelý už ten požadavek, protože to "['bbb']['ccc']" je v podstatě kus PHP kódu, takže by na to spolehlivě fungoval leda eval, což není dobré řešení.

Řešení by bylo na začátku mít místo řetězce pole těch indexů: array("bbb", "ccc"), pak to bude fungovat bez problému.
quatzael
Profil
Joker:
Ok, já s tím polem indexů problém nemám, ale vůbec nerozumím jak to bude fungovat s tím parse_str..


Joker:
Hele ale ten eval funguje!! tak to zase není až tak moc špatný řešení, ne?


Joker:
Díky moc!
Joker
Profil
quatzael:
Hele ale ten eval funguje!! tak to zase není až tak moc špatný řešení, ne?

Já vůl, jsem mohl čekat, že to takhle skončí.

Ok, já s tím polem indexů problém nemám
Tak v tom případě je to úplně jednoduché, ne?
$keys = array("aaa", "bbb", "ccc");
$val = $arr[$keys[0]][$keys[1]][$keys[2]];

Je to prostě index pole v proměnné.
Dan Charousek
Profil
Joker:
Něco mi říká, že počet indexů bude proměnlivý.

Já vůl, jsem mohl čekat, že to takhle skončí.
Snad ještě není pozdě :-)

quatzael:
Hele ale ten eval funguje!! tak to zase není až tak moc špatný řešení, ne?
Vlastně je.
Eval by se měl používat opravdu jen vzácně. Napíšeš-li do googlu why not use eval, dozvíš se víc. V tvém případě bych volil řešení, které navrhl Alphard asice implementovat vlastní getByKeys() funkci.

Ta funkce by mohla vypadat třeab takto:

<?php

function getByKeys(array $array, $keys) {

    $temp = $array;
    $length = count($keys);
    for($i = 0; $i < $length; $i++) {
        $key = $keys[$i];
        if(isset($temp[$key]) && ($i < $length - 1)) {
            $temp = $temp[$key];
        } elseif(isset($temp[$key])) {
            return $temp[$key];
        } else {
            throw new Exception("Argument out of range exception, undefined key '$key'");
        }
    }

}

$myArray = [
    "aaa" => [
        "bbb" => [
            "ccc" => "Hello world!"
        ]
    ]    
];

try {
    var_dump(getByKeys($myArray["aaa"], ["bbb", "ccc"]));
} catch(Exception $e) {
    echo "Error: " . $e->getMessage();
}
quatzael
Profil
Joker:
Dík, to sice funguje, ale asi neřeší ten problém, který jsem nezmínil, a to ten, že potřebuju, aby tam šlo takhle přidávat i delší ocas.
Tzn. když nebudu mít jen
$subarray = "['bbb']['ccc']";
ale delší např.
$subarray = "['bbb']['ccc']['ddd']";
Dan Charousek
Profil
quatzael:
To řeší moje funkce z [#7]
quatzael
Profil
Dan Charousek:
Asi jo, sorry, ještě jsem nerefreshoval, než jsem to psal.. Teď na to koukám..


Dan Charousek:
Ještě, abych upřesnil úplně o co mi jde.
Jedná se o proměnnou v metodě jedné třídy a je hodně rozvětvená.

$array['aaa']['branch1']['subbranch1'];
$array['aaa']['branch1']['subbranch2'];
$array['aaa']['branch2']['subbranch1'];
$array['aaa']['branch2']['subbranch2'];

A ještě víc všelijak rozvětvená struktura.. A já potřebuju každý tý větvi přiřadit konkrétní hodnotu.
S tím, že prakticky vždycky budu mít $array['aaa'] a potřebuju k ní připojit ten ocas a nastavit celé té větvi nějakou hodnotu.
Hlavně, aby se ta hodnota nastavila globálně a ne jenom uvnitř té funkce getByKeys.

To echo v příkladu jsem uvedl jen jako ukázku. Fakticky jde o přiřazování hodnot..
Dan Charousek
Profil
quatzael:
V tom případě by ta funkce mohla vypadat takto:

<?php

function iterate(&$array, $keys, $assign, $i = 0) {

    foreach($array as $key => &$value) {

        if(!isset($keys[$i+1]) && !empty($assign)) {
            $value = $assign;
            return;
        } elseif(!isset($keys[$i])) {
            return $value;
        }

        if($keys[$i] != $key) {
            continue;
        }

        $i++;
        if(is_array($value)) {
            return iterate($value, $keys, $assign, $i);
        } else {
            if(empty($assign)) {
                return $value;
            } else {
                $value = $assign;
            }
        }

    }

}

function findByKey(array &$array, array $keys, $assign = null) {
    return iterate($array, $keys, $assign);
}

$array = [
    "aaa" => [
        "bbb" => [
            "ccc" => "Hello world!"
        ]
    ]
];

echo "<pre>";
var_dump(findByKey($array["aaa"], ["bbb"])); // Vrátí Hello World!
findByKey($array, ["aaa", "bbb", "ccc"], "World Hello!");
var_dump($array); // Vytiskne pole - [aaa][bbb][ccc] bude mít novou hodnotu `World Hello!`
echo "</pre>";
Alphard
Profil
Tak to už je hodně velká lidová tvořivost :-) co třeba vrátit si referenci?
function &getByKeys(array &$input, array $keys) {
    $iter = &$input;
    foreach ($keys as $key) {
        if (!isset($iter[$key])) {
            throw new Exception("Argument out of range exception, undefined key '$key'");
        }
        $iter = &$iter[$key];
    }
    return $iter;
}
Dan Charousek
Profil
Alphard:
Děkuji,
toto mě posunulo o dost dál :)
Fisir
Profil
Reaguji na quatzaela:
Já teda nerad posílám kompletní hotová řešení, ale v tomto případě je to méně evil než eval.

function &access(&$array, $path){
    if(!is_array($array)){
        throw new \InvalidArgumentException('Argument $array expected to be an array.');
        return;
    }
    if(!is_string($path)){
        throw new \InvalidArgumentException('Argument $path expected to be a string.');
        return;
    }
    if(empty($path)){
        throw new \UnexpectedValueException('Argument $path cannot be empty.');
        return;
    }
    if(preg_match_all('/\[(["\']?)(.*?)(\\\\){0}\\1\]/u', $path, $matches, PREG_SET_ORDER) > 0){
        $result = &$array;
        foreach($matches as $match){
            $key = str_replace('\\'.$match[1], $match[1], $match[2]);
            if(isset($result[$key])){
                $result = &$result[$key];
            } else {
                throw new \UnexpectedValueException('Argument $path points to element which doesn\'t exist.');
                return null;
            }
        }
        return $result;
    } else {
        throw new \UnexpectedValueException('Argument $path is not a valid array pointer.');
        return null;
    }
}
Úprava: Opravena práce s escapováním.

Pak už stačí funkci access předhodit pole a tvojí proměnnou $subarray:
$array['aaa']['bbb']['ccc'] = "value";
$subarray = "['bbb']['ccc']";
echo access($array['aaa'], $subarray);

Poznámka pro pokročilé: funkce plně podporuje zpětné a externí upravování takto získaných hodnot, stačí si ji uložit do pomocné proměnné jako referenci.
Dan Charousek
Profil
Fisir:
Hezké :),
ale i tak si troufám říct, že zlatý bludišťák putuje Alphardovi :)
quatzael:
Každopádně doufám, že je jasné, že bychom se s kolegy nesnažili jen tak pro nic za nic, když bychom tě opravdu od toho evalu nechtěli odradit.
Fisir
Profil
Reaguji na Dana Charouska:
Já v podstatě dělám to samé jako Alphard, akorát podporuji quatzaelem v prvním příspěvku zmíněný zápis pole jako textový řetězec a kontroluji typy parametrů.
quatzael
Profil
Alphard, Dan Charousek, Fisir:
Kluci díky za návrhy. Já jsem to teda už vyřešil tím evalem. Každopádně jsem zkoumal Fisirovo řešení [#14].
Ta funkce, ale pouze vrací hodnotu $array['aaa']['bbb']['ccc'], ne?
Já jsem tady někde [#10] asi ne moc jasně upřesňoval, že potřebuju fakticky PŘIŘADIT hodnotu, ne jí echovat..

A je to celé ve třídě objektu, takže vlastně, když mám v metodě třídy tohle:

$value= "value";
$path = "['bbb']['ccc']";

potřebuju, aby něco co by mi potom nahradilo řádek:

$this->array['aaa']['bbb']['ccc'] = $value;

S tím, že pouze vím, že tam budu mít požito to $this->array['aaa']

Já jsem toto vlákno začal trochu obecně, protože mě nenapadlo, že to bude tak komplikovaný, tak se omlouvám..
Fisir
Profil
Reaguji na quatzaela:
Já jsem to teda už vyřešil tím evalem.
eval() není řešení. Je to bezpečnostní díra.

Ta funkce, ale pouze vrací hodnotu $array['aaa']['bbb']['ccc'], ne?
To není tak úplně pravda:
$value = 'value';
$path = '["bbb"]["ccc"]';
$array = array(
    'aaa' => array(
        'bbb' => array(
            'ccc' => 'test'
        )
    )
);

$variable = &access($array['aaa'], $path);
echo($variable); // vypíše 'test'
$variable = $value;
echo($array['aaa']['bbb']['ccc']); // vypíše 'value'

Všimni si, že výsledek funkce access() do proměnné $variable předávám jako referenci (znak &), takže se změnou její hodnoty změní i hodnota v původním poli (a naopak).
Dan Charousek
Profil
quatzael:
Musím si stát za tím, abys ten eval nepoužíval a zvážil použití jednoho z výše zmíněných kódů (doporučuji [#12]).
Fisirův kód také umí to, co požaduješ (viz. poznámka pod čarou v [#14])

Pokud by ses rozhodl použít Alphardovu funkci, vypadalo by to asi nějak takto:

$val = &getByKeys($this->array['aaa'], ['bbb', 'ccc']);
$val = "Hello World!"; // nastaví novou hodnotu.
var_dump($this->array['aaa']['bbb']['ccc']); // bude mít hodnotu `Hello World!`
Alphard
Profil
Joker [#4]:
Upřímně, tahle rada byla podle mě dost hloupá. Říct začátečníkovi o eval() v situaci, kdy ho zřejmě nepotřebuje a má své řešení blbě navržené*, nepovažuji za rozumné. Obvykle se najde někdo, kdo přispěchá s nevhodnou radou, přestože správné řešení je také velmi jednoduché :-)

*Proč si myslím, že je původní návrh špatný? V [#1] a [#3] quatzael popisuje svůj záměr traverzovat přes pole. Nezdá se mi, existoval objektvní důvod trvat na popsaném formátu (např. parsování jiných scriptů), je to jen přirozený důsledek zvyklosti z klasického PHP kódu a neznalosti správného návrhu.
Zdálo se mi docela rozumné zkusit mu „vnutit“ řešení [#2] Alphard, které považuji za návrhově přijatelné, je obecnější, bude mít podporu v editorech, bude lépe laditelné, atd. quatzael tady už docela dlouho pokládá základní otázky, tak proč ho trochu neučit...

Fisir [#14]:
Z mého pohledu neměl být quatzael v této cestě podporován, ale chápu, že to bylo myšleno jako lepší řešení než eval. K té funkci bych ale dodal, že zbytečně kombinuje 2 věci. Kdybych to psal já, vyčlením to do 2 funkcí. Jedna funkce bude parsovat vstupní řetězec, vrátí pole klíčů a předá ho druhé funkci ([#12] Alphard).

Mohu se zeptat, proč tam uvádíš return statement za throw? Je to unreachable code.

Alphard [#12]:
Kdyby mělo přiřazení fungovat jako setter, nevidím problém v přidání 3 parametru a přiřazení na 9. řádku.
Fisir
Profil
Reaguji na Alpharda:
Kdybych to psal já, vyčlením to do 2 funkcí.
Já bych z toho udělal třídu. Ale nejsem si úplně jistý, jestli by to quatzaelovi nějak pomohlo.

proč tam uvádíš return statement za throw?
Hm, to vážně netuším :-).
quatzael
Profil
Fisir:
eval() není řešení. Je to bezpečnostní díra.
To je bezpečnostní díra snad jen tehdy, když do toho vkládáš nějaké externí hodnoty. Tohle ale není vůbec můj případ.
Normálně bych eval nepoužil, ale tady se to zdá být jednoznačně nejvýhodnější, protože jedním řádkem udělám snadno to co jinak složitě třiceti.. [#14] Fisir

Alphard:
quatzael tady už docela dlouho pokládá základní otázky, tak proč ho trochu nemučit...“:o))
Díky za učení, hlavně ohledně toho předávání reference, to je pro mě novinka. Ale bude to potom správně fungovat? Když tam používáš ten foreach, tak se tam to pole postupně přepisuje, ne? Takže v části kdy se přepisuje hodnota $array['aaa']['bbb'] a přidává se k němu ['ccc'] tak se smaže případný obsah, který byl ve větvi $array['aaa']['bbb']['eee'].. A já samozřejmě potřebuju mít všechny ostatní větve naprosto nedotčeny..
tiso
Profil
quatzael: „A já samozřejmě potřebuju mít všechny ostatní větve naprosto nedotčeny..
To je taký problém to vyskúšať?

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: