2010-08-27 11 views
51

Ho un sacco di funzioni che hanno tipo suggerimento per matrici o utilizzare is_array() per verificare l'array di una variabile.Iterable object e hinting del tipo di array?

Ora sto iniziando a utilizzare oggetti che sono iterabili. Implementano Iterator o IteratorAggregate. Saranno accettati come matrici se passano attraverso il tipo di suggerimento o subiscono is_array()?

Se devo modificare il mio codice, c'è una sorta di generica is_iterable(), o devo fare qualcosa di simile:

if (is_array($var) OR $var instance_of Iterable OR $var instanceof IteratorAggregate) { ... } 

Quali altri iterabile interfacce sono là fuori?

risposta

74

Penso che intendiate l'istanza di Iterator, PHP non ha un'interfaccia Iterable. Tuttavia ha un'interfaccia Traversable. Iterator e IteratorAggregate entrambi estendono Traversable (e AFAIK sono gli unici a farlo).

Ma no, gli oggetti che implementano Traversable non superano il controllo is_array(), né esiste una funzione integrata is_iterable(). Un controllo è possibile utilizzare è

function is_iterable($var) { 
    return (is_array($var) || $var instanceof Traversable); 
} 

Per essere chiari, tutti oggetti PHP può essere iterato con foreach, ma solo alcuni di loro implementano Traversable. La funzione is_iterable presentata non rileverà quindi tutte le cose che foreach può gestire.

+22

'Iterator' e' IteratorAggregate' non implementano 'Traversable'. Sono interfacce e come tali non hanno alcuna implementazione. Esse * estendono * 'Traversabili'. A parte questo, +1 – Artefacto

+0

Non riesco a vedere cosa sto facendo male, ma non sembra funzionare per le classi che vanno bene con foreach: http://codepad.org/hi373LYg – MSpreij

+6

'foreach' funzionerà con le classi che non sono esempi di queste interfacce. Semplicemente scorre tutte le proprietà dell'istanza. Se si desidera un comportamento personalizzato, è necessario implementare 'Iterator' – NullUserException

1

Sfortunatamente non sarà possibile utilizzare suggerimenti tipo per questo e dovrà fare il roba is_array($var) or $var instanceof ArrayAccess . Questo è un problema noto ma ancora non è stato risolto. Almeno non funziona con PHP 5.3.2 che ho appena testato.

+0

[ 'ArrayAccess'] (http://php.net/manual/en/class.arrayaccess.php) è solo circa l'accesso diretto dalla sintassi "chiave di allineamento", non ha nulla a che fare con iterabilità. –

0

È possibile utilizzare il suggerimento del tipo se si passa all'utilizzo di oggetti iterabili.

protected function doSomethingWithIterableObject(Iterator $iterableObject) {} 

o

protected function doSomethingWithIterableObject(Traversable $iterableObject) {} 

Tuttavia, questo non può essere utilizzato per accettare oggetti iterabili e gli array allo stesso tempo. Se si vuole veramente fare che potrebbe provare a costruire una funzione wrapper qualcosa di simile:

// generic function (use name of original function) for old code 
// (new code may call the appropriate function directly) 
public function doSomethingIterable($iterable) 
{ 
    if (is_array($iterable)) { 
     return $this->doSomethingIterableWithArray($iterable); 
    } 
    if ($iterable instanceof Traversable) { 
     return $this->doSomethingIterableWithObject($iterable); 
    } 
    return null; 
} 
public function doSomethingIterableWithArray(array $iterable) 
{ 
    return $this->myIterableFunction($iterable); 
} 
public function doSomethingIterableWithObject(Iterator $iterable) 
{ 
    return $this->myIterableFunction($iterable); 
} 
protected function myIterableFunction($iterable) 
{ 
    // no type checking here 
    $result = null; 
    foreach ($iterable as $item) 
    { 
     // do stuff 
    } 
    return $result; 
} 
11

in realtà ho dovuto aggiungere un assegno di stdClass, come esempi di stdClass funzionano nei cicli foreach, ma stdClass non implementa Traversable :

function is_iterable($var) { 
    return (is_array($var) || $var instanceof Traversable || $var instanceof stdClass); 
} 
+9

Tutti gli oggetti possono essere 'foreach''d, ma così facendo è spesso non intenzionale. – Brilliand

+1

Non tutti gli oggetti sono un 'instanceof stdClass', ma come sottolineato da @Brilliand, tutti gli oggetti possono essere' foreach''d. In molti casi verificherai per vedere se effettivamente vuoi 'foreach' it, come in quando' is_array ($ var) 'restituisce true o quando' $ var instanceof Traversable' restituisce true. Se in realtà vuoi eseguire il 'foreach' su tutto ciò che può essere' foreach''d (niente di sbagliato in questo finché capisci cosa stai facendo e perché), allora sarebbe meglio sostituire l'ultima parte di il codice con 'is_object ($ var)' –

+0

La prossima domanda è: perché stai usando stdClass? Quasi nessun vantaggio, molti svantaggi. – CommandZ

4

io uso una (e forse un po 'hacker) modo semplice per testare per "iterabilità".

function is_iterable($var) { 
    set_error_handler(function ($errno, $errstr, $errfile, $errline, array $errcontext) 
    { 
     throw new \ErrorException($errstr, null, $errno, $errfile, $errline); 
    }); 

    try { 
     foreach ($var as $v) { 
      break; 
     } 
    } catch (\ErrorException $e) { 
     restore_error_handler(); 
     return false; 
    } 
    restore_error_handler(); 
    return true; 
} 

Quando si tenta di ciclo una variabile non iterabile, PHP getta un avvertimento. Impostando un gestore di errori personalizzato prima del tentativo di iterare, è possibile trasformare un errore in un'eccezione che consente di utilizzare un blocco try/catch. Successivamente si ripristina il precedente gestore di errori per non interrompere il flusso del programma.

Ecco un piccolo test case (testato in PHP 5.3.15):

class Foo { 
    public $a = 'one'; 
    public $b = 'two'; 
} 

$foo = new Foo(); 
$bar = array('d','e','f'); 
$baz = 'string'; 
$bazinga = 1; 
$boo = new StdClass();  

var_dump(is_iterable($foo)); //boolean true 
var_dump(is_iterable($bar)); //boolean true 
var_dump(is_iterable($baz)); //boolean false 
var_dump(is_iterable($bazinga)); //bolean false 
var_dump(is_iterable($boo)); //bolean true 
+2

Ho eseguito una versione leggermente modificata su 3v4l.org che funziona su PHP 5.0+: http://3v4l.org/ITNF9. Anche stdClass passa su tutti loro. –

+0

Sai se c'è qualcosa che il tuo metodo cattura, che [questa risposta] (http://stackoverflow.com/a/12395684/249538) non lo fa? – goat

+2

@rambocoder qualsiasi oggetto che non "estenda" StdClass né implementa esplicitamente Traversable. Ad esempio, alcune librerie PECL, come PHP Imuttable Extension, usano costrutti C piuttosto che oggetti PHP. Questi oggetti sono iterabili, ma la funzione sopra non li riconosce come tali, poiché non sono basati su PHP Object Construct a livello C. – Tivie

21

PHP 7.1.0 has introduced la iterable pseudo-type and the is_iterable() function, appositamente progettato a tale scopo:

Questo [...] propone un nuovo iterable pseudo-type. Questo tipo è analogo a callable, accettando più tipi invece di un singolo tipo.

iterable accetta qualsiasi array o oggetto che implementa Traversable. Entrambi questi tipi sono iterabili utilizzando foreach e possono essere utilizzati con yield da un generatore.

function foo(iterable $iterable) { 
    foreach ($iterable as $value) { 
     // ... 
    } 
} 

Questo [...] aggiunge anche una funzione is_iterable() che restituisce un valore booleano: true se un valore è iterabile e verrà accettato dalla pseudo-tipo iterable, false per altri valori.

var_dump(is_iterable([1, 2, 3])); // bool(true) 
var_dump(is_iterable(new ArrayIterator([1, 2, 3]))); // bool(true) 
var_dump(is_iterable((function() { yield 1; })())); // bool(true) 
var_dump(is_iterable(1)); // bool(false) 
var_dump(is_iterable(new stdClass())); // bool(false) 
+0

Grazie per avermelo fatto notare! – user151841