[PHP] Trasformazione ciclo in funzione ricorsiva

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
TommyB1992
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 862
Iscrizione: domenica 7 luglio 2013, 15:55
Desktop: GNU/Linux
Distribuzione: Ubuntu 22.04.2 LTS
Sesso: Maschile

[PHP] Trasformazione ciclo in funzione ricorsiva

Messaggio da TommyB1992 »

Codice: Seleziona tutto

	static public function GET($key, $deep = 0) {
		if (empty(self::$_get[$key])) {
			self::$_get[$key] = null;
			if (isset($_GET[$key])) {
				if (is_string($_GET[$key]) && $deep == 0) {
					self::$_get[$key] = $_GET[$key];
				}
				elseif (is_array($_GET[$key])) {
					self::$_get[$key] = $_GET[$key];
					foreach ($_GET[$key] as $i => $value) {						
						if ($deep == 1 && is_array($value)) {
							self::$_get[$key] = null;
						}
						elseif (!is_string($value)) {
							foreach ($value as $i2 => $value2) {
								if ($deep == 2 && is_array($value2)) {
									self::$_get[$key] = null;
								}
								elseif (!is_string($value2)) {
									foreach ($value2 as $i3 => $value3) {
										if ($deep == 3 && is_array($value3)) {
											self::$_get[$key] = null;
										}
										elseif (is_string($value3)) {
											// ... infinite loop
										}
									}
								}
							}
						}
					}
				}
			}
		}
		
		return self::$_get[$key];
Come faccio a trasformare tutto il ciclo foreach in una funzione ricorsiva, anche perchè non vorrei mettere limiti a $deep e ovviamente trovo inutile creare cicli dentro cicli quando può essere fatto con una funzione ricorsiva.


Il funzionamento è questo:
index.php?test[][]=1
print_r(self::GET('a', 1));

DEBUG:
null

-----------------------------------

index.php?test[]=1
print_r(self::GET('a', 1));

DEBUG:
Array
(
[0] => 1
)

-----------------------------------

index.php?test[][]=1
print_r(self::GET('a', 2));

DEBUG:
Array
(
[0] => Array
(
[0] => 2
)
)

-----------------------------------

index.php?test[][][]=1
print_r(self::GET('a', 2));

DEBUG:
null
Avatar utente
Zoff
Moderatore Globale
Moderatore Globale
Messaggi: 33338
Iscrizione: mercoledì 10 ottobre 2007, 22:36

Re: [PHP] Trasformazione ciclo in funzione ricorsiva

Messaggio da Zoff »

In pratica vuoi copiare un array e tutti i suoi sotto elementi, giusto?

Codice: Seleziona tutto

function array_copy(array $arr) {
    $newArray = [];
    foreach($arr as $key => $value) {
        //Copia array
        if(is_array($value)) $newArray[$key] = array_copy($value);
        //Copia oggetto
        else if(is_object($value)) $newArray[$key] = clone $value;
        //Copia valore (semi)primitivo
        else $newArray[$key] = $value;
    }
    return $newArray;
}
Oppure se ti piacciono le funzioni brevi ed illeggibili:

Codice: Seleziona tutto

function array_copy(array $arr) {
    return array_map(function($v){ return is_array($v) ? array_copy($v) : (is_object($v) ? clone $v : $v); },$arr);
}
Prima di aprire una discussione leggi le Guide, poi vedi se c'è un HowTo nel Wiki e fai una ricerca nel Forum!
Applica semplicemente il [Risolto]! Prova: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=548821
Vuoi qualcosa di piu' dal forum? Prova i miei script: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=597066
TommyB1992
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 862
Iscrizione: domenica 7 luglio 2013, 15:55
Desktop: GNU/Linux
Distribuzione: Ubuntu 22.04.2 LTS
Sesso: Maschile

Re: [PHP] Trasformazione ciclo in funzione ricorsiva

Messaggio da TommyB1992 »

Non esattamente, è un tentativo di prevenire attacchi Full Path Disclosure senza dover abilitare un error_repoting adatto o fare sempre:
if (is_array($_POST['x'])) // no
Avatar utente
Zoff
Moderatore Globale
Moderatore Globale
Messaggi: 33338
Iscrizione: mercoledì 10 ottobre 2007, 22:36

Re: [PHP] Trasformazione ciclo in funzione ricorsiva

Messaggio da Zoff »

Non vedo il nesso tra gli attacchi e l'array.

L'input validation di deve fare SEMPRE ed in ogni caso.

NB: Nei tuoi esempi quale sarebbe il collegamento tra il parametro test e la stringa 'a' che usi nel metodo self:GET()?
Prima di aprire una discussione leggi le Guide, poi vedi se c'è un HowTo nel Wiki e fai una ricerca nel Forum!
Applica semplicemente il [Risolto]! Prova: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=548821
Vuoi qualcosa di piu' dal forum? Prova i miei script: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=597066
TommyB1992
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 862
Iscrizione: domenica 7 luglio 2013, 15:55
Desktop: GNU/Linux
Distribuzione: Ubuntu 22.04.2 LTS
Sesso: Maschile

Re: [PHP] Trasformazione ciclo in funzione ricorsiva

Messaggio da TommyB1992 »

Codice: Seleziona tutto

?a[]=1

echo $_GET['a'];
Questo da errore.

Codice: Seleziona tutto

?a[]=1

echo self::GET('a');
Questo no

Codice: Seleziona tutto

?a[][]=1

if (is_array($_GET['a'])) {
  echo $_GET['a'][0];
}
Questo da errore.

Codice: Seleziona tutto

?a[][]=1

echo self::GET('a', 1); // questo no
$deep corrisponde alla multidimensione dell'array, se è maggiore o minore l'array ritorna null (in questa versione di funzione però è scritta male perchè non controlla se è minore), me ne sono accorto dopo e l'ho corretta successivamente alla scrittura del post.
Avatar utente
Zoff
Moderatore Globale
Moderatore Globale
Messaggi: 33338
Iscrizione: mercoledì 10 ottobre 2007, 22:36

Re: [PHP] Trasformazione ciclo in funzione ricorsiva

Messaggio da Zoff »

Perché delegare il controllo ad una funzione ricorsiva?

Tu sai esattamente cosa vuoi, perché dare il controllo ad una funzione generica?
Tra l'altro, quali sono i casi d'uso in cui vado a leggere un array multi dimensionale da $_GET?
Quando useresti $deep>2?

Secondo me fai prima a crearti delle funzioni tipo:

Codice: Seleziona tutto

<?php

function getContainer(string $method = 'GET'){
    switch(strtoupper($method)){
        case 'POST':
            return $_POST;
        default:
        case 'GET':
            return $_GET;
    }
}

function getParam(string $name, string $method = 'GET'){
    $container = getContainer($method);
    return isset($container[$name]) ? $container[$name] : null;
}

function getString(string $name, string $method = 'GET'){
    $raw = getParam($name, $method);
    if( null === $raw || ! is_string($raw) ){
        return null;
    }
    return (string)$raw;
}

function getInteger(string $name, string $method = 'GET'){
    $raw = getParam($name, $method);
    if( null === $raw || ! ctype_digit($raw) ){
        return null;
    }
    return (int)$raw;
}

function getArray(string $name, string $method = 'GET'){
    $raw = getParam($name, $method);
    if( null === $raw || !is_array($raw) ){
        return null;
    }
    return (array)$raw;
}

// ?a=1
$_GET['a'] = '1';
echo PHP_EOL . '?a=1' . PHP_EOL;
var_dump( getString('a') );
var_dump( getInteger('a') );
var_dump( getArray('a') );

// ?a[]=1
$_GET['a'] = ['1'];
echo PHP_EOL . '?a[]=1' . PHP_EOL;
var_dump( getString('a') );
var_dump( getInteger('a') );
var_dump( getArray('a') );


// ?a[][]=1
$_GET['a'] = [['1']];
echo PHP_EOL . '?a[][]=1' . PHP_EOL;
var_dump( getString('a') );
var_dump( getInteger('a') );
var_dump( getArray('a') );
EDIT: Al più puoi definire una tua convenzione per accedere agli elementi innestati.
Tipo la notazione puntata:

Codice: Seleziona tutto

<?php

function getContainer(string $method = 'GET'){
    switch(strtoupper($method)){
        case 'POST':
            return $_POST;
        default:
        case 'GET':
            return $_GET;
    }
}

function getParam(string $name, string $method = 'GET'){
    $container = getContainer($method);
    if( strpos($name,'.') === false ){
        return isset($container[$name]) ? $container[$name] : null;
    }
    $names = explode('.',$name);
    foreach( $names as $name ){
        if( !is_array($container) || !isset($container[$name]) ){
            return null;
        }
        $container = $container[$name];
    }
    return $container;
}

function getString(string $name, string $method = 'GET'){
    $raw = getParam($name, $method);
    if( null === $raw || ! is_string($raw) ){
        return null;
    }
    return (string)$raw;
}

function getInteger(string $name, string $method = 'GET'){
    $raw = getParam($name, $method);
    if( null === $raw || ! ctype_digit($raw) ){
        return null;
    }
    return (int)$raw;
}

function getArray(string $name, string $method = 'GET'){
    $raw = getParam($name, $method);
    if( null === $raw || !is_array($raw) ){
        return null;
    }
    return (array)$raw;
}

// ?a=1
$_GET['a'] = '1';
echo PHP_EOL . '?a=1' . PHP_EOL;
var_dump( getString('a') );
var_dump( getInteger('a') );
var_dump( getArray('a') );
var_dump( getString('a.b') );
var_dump( getInteger('a.b') );
var_dump( getArray('a.b') );
var_dump( getString('a.b.0') );
var_dump( getInteger('a.b.0') );
var_dump( getArray('a.b.0') );

// ?a[]=1
$_GET['a'] = ['1'];
echo PHP_EOL . '?a[]=1' . PHP_EOL;
var_dump( getString('a') );
var_dump( getInteger('a') );
var_dump( getArray('a') );
var_dump( getString('a.b') );
var_dump( getInteger('a.b') );
var_dump( getArray('a.b') );
var_dump( getString('a.b.0') );
var_dump( getInteger('a.b.0') );
var_dump( getArray('a.b.0') );


// ?a[][]=1
$_GET['a'] = [['1']];
echo PHP_EOL . '?a[][]=1' . PHP_EOL;
var_dump( getString('a') );
var_dump( getInteger('a') );
var_dump( getArray('a') );
var_dump( getString('a.b') );
var_dump( getInteger('a.b') );
var_dump( getArray('a.b') );
var_dump( getString('a.b.0') );
var_dump( getInteger('a.b.0') );
var_dump( getArray('a.b.0') );

// ?a[b][]=1
$_GET['a'] = ['b' => ['1']];
echo PHP_EOL . '?a[b][]=1' . PHP_EOL;
var_dump( getString('a') );
var_dump( getInteger('a') );
var_dump( getArray('a') );
var_dump( getString('a.b') );
var_dump( getInteger('a.b') );
var_dump( getArray('a.b') );
var_dump( getString('a.b.0') );
var_dump( getInteger('a.b.0') );
var_dump( getArray('a.b.0') );
 
Prima di aprire una discussione leggi le Guide, poi vedi se c'è un HowTo nel Wiki e fai una ricerca nel Forum!
Applica semplicemente il [Risolto]! Prova: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=548821
Vuoi qualcosa di piu' dal forum? Prova i miei script: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=597066
TommyB1992
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 862
Iscrizione: domenica 7 luglio 2013, 15:55
Desktop: GNU/Linux
Distribuzione: Ubuntu 22.04.2 LTS
Sesso: Maschile

Re: [PHP] Trasformazione ciclo in funzione ricorsiva

Messaggio da TommyB1992 »

Tu sai esattamente cosa vuoi, perché dare il controllo ad una funzione generica?
Tra l'altro, quali sono i casi d'uso in cui vado a leggere un array multi dimensionale da $_GET?
Quando useresti $deep>2?
Perchè io lo so, ma gli altri no, volevo rilasciare un progetto open source.
Avatar utente
Zoff
Moderatore Globale
Moderatore Globale
Messaggi: 33338
Iscrizione: mercoledì 10 ottobre 2007, 22:36

Re: [PHP] Trasformazione ciclo in funzione ricorsiva

Messaggio da Zoff »

no no no, se tu passi un argomento e vuoi usarlo DEVI sapere cosa contiene.
Se un programmatore usa dei valori provenienti dall'utente senza conoscere (e controllare) il loro tipo ha sbagliato hobby/lavoro.
Per cui puoi farti delle funzioni per leggere il parametro sapendo che tipo ti serve come ti ho fatto vedere sopra.
Prima di aprire una discussione leggi le Guide, poi vedi se c'è un HowTo nel Wiki e fai una ricerca nel Forum!
Applica semplicemente il [Risolto]! Prova: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=548821
Vuoi qualcosa di piu' dal forum? Prova i miei script: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=597066
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: Google [Bot] e 4 ospiti