Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

João Batista Neto

1.5 Interfaces SPL - Iterator-IteratorAggregate

Recommended Posts

Bom, hoje começaremos falar sobre a SPL - Standard PHP Library, uma biblioteca com várias classes e interfaces de extrema utilidade, mas pouco utilizada. Inicialmente falaremos sobre a Iterator e sua colega IteratorAggregate que são, juntas, participantes de um design pattern.

Como Iterator é um design pattern, vamos descrevê-lo como tal:

Iterator Imagem Postada Comportamental

Conhecido também como:

Cursor:

Motivação:

 

Acessar os elementos de um objeto sem violar o encapsulamento, reduzir, do objeto que possui a lista de elementos, a responsabilidade de ter que implementar várias formas de se percorrer esses elementos.
O objeto lista fica responsável apenas pela adição, remoção e manutenção dos elementos enquanto o objeto iterator fica responsável apenas por permitir que seja possível percorrer esses elementos, seja em uma direção ou outra.


Aplicabilidade:

 

 

Acessar o conteúdo de um objeto agregado sem violar o encapsulamento.
Suportar múltiplos percursos (iteração em várias direções)
Fornecer uma interface comum que percorra diferentes estruturas (vários objetos diferentes, comportam-se iguais: polimorfismo)


Participantes:

 

 

Iterator:

 

Interface para acessar e percorrer os elementos

ConcreteIterator:

 

Implementação da interface Iterator que permitirá que os elementos sejam percorridos em uma direção qualquer.
Mantém o controle da posição do elemento atual.

Aggregate:

 

 

Define uma interface para criação de um objeto Iterator.

ConcreteAggregate:

 

 

Implementação da interface Aggregate que permitirá que o objeto retorne uma instância do objeto ConcreteIterator adequado.

 

 

Extrutura:

 

 

 

Imagem Postada

Provavelmente, o uso mais comum de Iteratores no PHP é com matrizes onde, utilizando-se um loop for ou foreach percorremos os elementos da matriz, mas, esse definitivamente não é o único uso de Iteratores. Imaginem que tenhamos um arquivo CSV (Comma Separated Values), cada linha desse arquivo representa um objeto de dados que precisamos trabalhar; Se a responsabilidade de iteração ficar sobre o parser CSV inflaremos a classe com métodos de iteração e, se amanhã, precisarmos iterar esse CSV em ordem inversa, teremos ainda mais métodos inflando nossa classe e, cada vez que precisarmos iterar nosso objeto de uma forma diferente, precisaremos editar o código do parser para que ele passe a suportar essa nova ordem.
Quando terminarmos de falar sobre a SPL, falaremos sobre modelagem, mas, até lá, vamos pensar de forma simplista -- Um CSV é, na verdade, um arquivo contendo valores separados por vírgulas, então, vamos primeiro pensar como arquivo.

As operações básicas que efetuamos sobre um arquivo são:

1. Abrir o arquivo.
2. Avançar o ponteiro do arquivo para uma posição específica.
3. Recuperar a posição atual do ponteiro do arquivo
4. Verificar se o arquivo chegou ao final
5. Ler 1 ou mais bytes do arquivo.
6. Ler uma linha inteira do arquivo.
7. Gravar no arquivo.
8. Fechar o arquivo.

O PHP nos oferece as seguintes operações, respectivamente, para cada uma das operações básicas:

1. fopen()
2. fseek()
3. ftell()
4. feof()
5. fread()
6. fgets()
7. fwrite()
8. fclose()

Então, vamos construir a interface de um arquivo:

/**
* Interface para um manipulador de arquivos
*/
interface File extends IteratorAggregate {
/**
* Abre o arquivo
* @return boolean TRUE se o arquivo tiver sido aberto com sucesso
*/
public function open();

/**
* Move o ponteiro do arquivo para uma posição específica
* @param integer $offset Quantidade de bytes que será movido
* @param integer $whence Define a forma q o ponteiro será movido
* @return boolean TRUE se o ponteiro do arquivo tiver sido movido com sucesso
*/
public function seek( $offset , $whence = SEEK_SET );

/**
* Recupera a posição atual do ponteiro do arquivo
* @return integer A posição atual do ponteiro do arquivo
*/
public function tell();

/**
* Verifica se o ponteiro do arquivo chegou ao final do arquivo
* @return boolean
*/
public function eof();

/**
* Lê uma quantidade de informação do arquivo definida por $bytes
* @param integer $bytes Número de bytes que serão lidos do arquivo
* @return string Os dados lidos do arquivo
*/
public function read( $bytes );

/**
* Lê uma linha inteira do arquivo
* @return string A linha lida do arquivo
*/
public function getLine();

/**
* Grava uma informação no arquivo
* @param string $data A informação que será gravada no arquivo
* @return boolean TRUE se os dados tiverem sido gravados com sucesso
*/
public function write( $data );

/**
* Fecha o arquivo
*/
public function close();
}



Percebam que, em nenhum momento, foi definido qualquer método para iterar o arquivo, definimos apenas as operações básicas, percebam também que nossa interface File estende a interface IteratorAggregate, herdando assim o método getIterator(). Como possuímos várias operações que serão sempre comuns para qualquer implementação de File, vamos definir uma classe abstrata que implementará o que é comum, facilitando assim futuras implementações:

/**
* Classe base para implementações da interface File com o objetivo de simplificar
* a implementação dessa interface.
* @abstract
*/
abstract class AbstractFile implements File {
/**
* Ponteiro do arquivo retornado por fopen()
* @var resource
*/
private $handler;

/**
* Nome do arquivo
* @var string
*/
private $file;

/**
* Constroi um novo objeto definindo o nome do arquivo que será trabalhado
* @param string $file
*/
public function __construct( $file ){
$this->file = $file;
}

/**
* Abre o arquivo
* @return boolean TRUE se o arquivo tiver sido aberto com sucesso
*/
public function open(){
$handler = fopen( $this->file , 'r+' );

if ( $handler !== false ){
$this->handler =& $handler;
return true;
}

return false;
}

/**
* Move o ponteiro do arquivo para uma posição específica
* @param integer $offset Quantidade de bytes que será movido
* @param integer $whence Define a forma q o ponteiro será movido
* @return boolean TRUE se o ponteiro do arquivo tiver sido movido com sucesso
*/
public function seek( $bytes , $whence = SEEK_SET ){
$ret = -1;

if ( $this->testOpened() ){
$ret = fseek( $this->handler , $bytes , $whence );
}

return $ret != -1;
}

/**
* Recupera a posição atual do ponteiro do arquivo
* @return integer A posição atual do ponteiro do arquivo
*/
public function tell(){
if ( $this->testOpened() ){
return ftell( $this->handler );
}

return false;
}

/**
* Verifica se o ponteiro do arquivo está no final
* @return boolean
*/
public function eof(){
if ( $this->testOpened() ){
return feof( $this->handler );
}

return true;
}

/**
* Lê uma quantidade de informação do arquivo definida por $bytes
* @param integer $bytes Número de bytes que serão lidos do arquivo
* @return string Os dados lidos do arquivo
*/
public function read( $bytes ){
if ( $this->testOpened() ){
return fread( $this->handler , $bytes );
}

return null;
}

/**
* Lê uma linha inteira do arquivo
* @return string A linha lida do arquivo
*/
public function getLine(){
if ( $this->testOpened() ){
return fgets( $this->handler );
}

return null;
}

/**
* Grava uma informação no arquivo
* @param string $data A informação que será gravada no arquivo
* @return boolean TRUE se os dados tiverem sido gravados com sucesso
*/
public function write( $data ){
if ( $this->testOpened() ){
$length = strlen( $data );
$size = fwrite( $this->handler , $data , $length );

return $length === $size;
}

return false;
}

/**
* Fecha o arquivo
*/
public function close(){
if ( $this->testOpened() ){
fclose( $this->handler );
}
}

/**
* Verifica se o arquivo já foi aberto
* @return boolean TRUE se o arquivo tiver sido aberto
* @throws RuntimeException Se o arquivo ainda não tiver sido aberto
*/
protected function testOpened(){
if ( is_null( $this->handler ) ){
throw new RuntimeException( 'O arquivo precisa ser aberto antes.' );
}

return true;
}
}



Percebam que nossa AbstractFile implementou quase todos os métodos, o único que ficou faltando foi justamente o getIterator() da interface SPL IteratorAggregate, isso porque, as operações com arquivos são muito semelhantes para qualquer implementação mas, um Iterator já é algo mais específico, então, deixamos que as implementações sejam responsáveis por esse método.
Como estamos falando sobre CSV, vamos agora para nossa implementação propriamente dita:

/**
* Classe para leitura de arquivos CSV
*/
class CSV extends AbstractFile {
/**
* Delimitador de colunas do CSV
* @var string
*/
private $delimiter;

/**
* Caracter utilizado para envolver colunas com caracteres especiais
* @var string
*/
private $enclosure;

/**
* Cria uma instância de CSV
* @param string $file O nome do arquivo
* @param string $delimiter Delimitador de colunas do CSV
* @param string $enclosure Caracter utilizaro para envolvar colunas com caracteres especiais
*/
public function __construct( $file , $delimiter = ',' , $enclosure = '"' ){
parent::__construct( $file );

$this->delimiter = $delimiter;
$this->enclosure = $enclosure;
}

/**
* Recupera um Iterator para um arquivo CSV
* @return CSVIterator
*/
public function getIterator(){
return new CSVIterator( $this );
}

/**
* Recupera uma linha do arquivo CSV
* @return array
*/
public function getCSVLine(){
return str_getcsv( $this->getLine() , $this->delimiter , $this->enclosure );
}
}



Bom, como é notório, nossa abstração foi eficiente, em poucas linhas fomos capazes de implementar um leitor de CSV, tudo o que precisamos foi um método para interpretar a linha retornada pelo método getLine() e um método getIterator() que retorna uma instância de CSVIterator. Falta agora só o CSVIterator:

/**
* Iterator de arquivos CSV
*/
class CSVIterator implements Iterator {
/**
* Manipulador do arquivo
* @var CSV
*/
private $csv;

/**
* Número da linha atual
* @var integer
*/
private $key;

/**
* Linha atual
* @var array
*/
private $current;

/**
* Constroi um novo Iterator de CSV
* @param CSV $csv O manipulador do arquivo CSV
*/
public function __construct( CSV $csv ){
$this->csv = $csv;
$this->rewind();
}

/**
* Recupera o elemento atual do Iterator
* @return array
*/
public function current(){
return $this->current;
}

/**
* Recupera a linha atual
* @return integer
*/
public function key(){
return $this->key;
}

/**
* Avança para o próximo elemento
*/
public function next(){
$pos = $this->csv->tell();
$this->current = $this->csv->getCSVLine();
++$this->key;
}

/**
* Reinicia o Iterator
*/
public function rewind(){
$this->key = 0;
$this->current = array();
$this->csv->seek( 0 );
$this->next();
}

/**
* Verifica se o Iterator é válido
* @return boolean
*/
public function valid(){
return !$this->csv->eof();
}
}



Agora, fica tudo muito simples para recuperar os dados de um CSV, imaginem o seguinte arquivo:

SPL,AppendIterator,The AppendIterator classSPL,ArrayIterator,The ArrayIterator classSPL,CachingIterator,The CachingIterator classSPL,DirectoryIterator,The DirectoryIterator classSPL,EmptyIterator,The EmptyIterator classSPL,FilesystemIterator,The FilesystemIterator classSPL,FilterIterator,The FilterIterator classSPL,GlobIterator,The GlobIterator classSPL,InfiniteIterator,The InfiniteIterator classSPL,IteratorIterator,The IteratorIterator classSPL,LimitIterator,The LimitIterator classSPL,MultipleIterator,The MultipleIterator classSPL,NoRewindIterator,The NoRewindIterator classSPL,ParentIterator,The ParentIterator classSPL,RecursiveArrayIterator,The RecursiveArrayIterator classSPL,RecursiveCachingIterator,The RecursiveCachingIterator classSPL,RecursiveDirectoryIterator,The RecursiveDirectoryIterator classSPL,RecursiveFilterIterator,The RecursiveFilterIterator classSPL,RecursiveIteratorIterator,The RecursiveIteratorIterator classSPL,RecursiveRegexIterator,The RecursiveRegexIterator classSPL,RecursiveTreeIterator,The RecursiveTreeIterator classSPL,RegexIterator,The RegexIterator classSPL,SimpleXMLIterator,The SimpleXMLIterator class


Como podem ver, uma lista CSV contendo os vários Iteratores definidos pela SPL, vamos utilizar o código acima para Iterá-lo:

$csv = new CSV( 'iteratoresSPL.csv' );
$csv->open();

foreach ( $csv as $linha => $dados ){
var_dump( $dados );
}



A saída:

 

array(3) { [0]=> string(3) "SPL" [1]=> string(14) "AppendIterator" [2]=> string(24) "The AppendIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(13) "ArrayIterator" [2]=> string(23) "The ArrayIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(15) "CachingIterator" [2]=> string(25) "The CachingIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(17) "DirectoryIterator" [2]=> string(27) "The DirectoryIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(13) "EmptyIterator" [2]=> string(23) "The EmptyIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(18) "FilesystemIterator" [2]=> string(28) "The FilesystemIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(14) "FilterIterator" [2]=> string(24) "The FilterIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(12) "GlobIterator" [2]=> string(22) "The GlobIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(16) "InfiniteIterator" [2]=> string(26) "The InfiniteIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(16) "IteratorIterator" [2]=> string(26) "The IteratorIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(13) "LimitIterator" [2]=> string(23) "The LimitIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(16) "MultipleIterator" [2]=> string(26) "The MultipleIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(16) "NoRewindIterator" [2]=> string(26) "The NoRewindIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(14) "ParentIterator" [2]=> string(24) "The ParentIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(22) "RecursiveArrayIterator" [2]=> string(32) "The RecursiveArrayIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(24) "RecursiveCachingIterator" [2]=> string(34) "The RecursiveCachingIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(26) "RecursiveDirectoryIterator" [2]=> string(36) "The RecursiveDirectoryIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(23) "RecursiveFilterIterator" [2]=> string(33) "The RecursiveFilterIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(25) "RecursiveIteratorIterator" [2]=> string(35) "The RecursiveIteratorIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(22) "RecursiveRegexIterator" [2]=> string(32) "The RecursiveRegexIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(21) "RecursiveTreeIterator" [2]=> string(31) "The RecursiveTreeIterator class"}array(3) { [0]=> string(3) "SPL" [1]=> string(13) "RegexIterator" [2]=> string(23) "The RegexIterator class"}


Bom, por hora é só, no próximo artigo da série PHP Orientado a Objetos continuaremos falando das interfaces SPL.

Imagem Postada

Índice Imagem Postada

 

 

 

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Quando terminarmos de falar sobre a SPL, falaremos sobre modelagem

 

Muito bom. Apesarde avançado, ficou fácil de entender. Mas poderia explicar o diagrama de modelagem?

 

Como eu disse, falaremos de modelagem e UML ao término da SPL.

 

Agora, se o tópico de hoje ficou complexo, não deixem de perguntar caso haja alguma dúvida, responderei cada pergunta que for feita.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Parabéns João Batista Neto pela inicitiva do Curso http://forum.imasters.com.br/public/style_emoticons/default/clap.gif

 

Eu em particular entendi com muita precisão e clareza até a 1.4.3 mas agora ficou um pouco técnico e tornando difícil a compreensão.

 

"A tarefa é difícil, dificílima!"

 

Mas fico aguardando os próximos...

 

Abraços!!!!

Compartilhar este post


Link para o post
Compartilhar em outros sites

técnico e tornando difícil a compreensão.

 

Senhores,

 

Se eu não souber exatamente onde está a dificuldade, eu não conseguirei:

 

1. Explicar a dúvida

2. Simplificar nos próximos tópicos

 

Preciso que me digam, onde ficou difícil ?

 

No código de exemplo ?

 

Se for o código de exemplo, procurarei postar outros, quantos forem necessários para que não restem dúvidas. Mas é importante perceber que agora estamos falando da SPL, isso significa que eu não posso modificar as interfaces que já estão prontas, tudo o que podemos fazer utilizar exemplos mais simples para ilustrar o uso.

Compartilhar este post


Link para o post
Compartilhar em outros sites

João, se não for dar muito trabalho, tenho uma sugestão:

 

- Comentar o código

- Linkar a Library no manual (pra mim é desnecessário, mas tem gente que precisa)

 

Uma vez que a codificação deixou de ser palpável (como "atirei o pau no gato"), alguns termos técnicos acabam carecendo de informações adicionais.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Sobre comentar o código eu concordo com o Evandro.

 

Principal, mas não exclusivamente, quando há o type hinting de classes ou interfaces que, em uso, significa passa um objeto como argumento de um método, usam-se métodos desse objeto passado o que, na leitura do artigo, se faz necessário "subir e descer" algumas vezes para se completar um raciocínio.

 

Evando, sobre linkar a library seria ao longo de todo artigo, tal qual o GeSHi faz ao gerar HTML colorido para os códigos? Ou apenas no início ou fim do artigo linkar os recursos utilizados?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Muito bom http://forum.imasters.com.br/public/style_emoticons/default/clap.gif

 

Duas dúvidas:

 

        public function open(){
                $handler = fopen( $this->file , 'r+' );

                if ( $handler !== false ){
                        $this->handler =& $handler;
                        return true;
                }

                return false;
        }

if ( $handler !== false ){

 

No que !== é diferente de != ?

 

$this->handler =& $handler;

 

=& ta mandando o endereço de memória ou algo assim? (lembrei de linguagem C)

Compartilhar este post


Link para o post
Compartilhar em outros sites

if ( $handler !== false ){

 

No que !== é diferente de != ?

 

= atribui

== compara se é igual (mesmo valor)

=== compara se é idêntico (mesmo valor e mesmo tipo)

! significa negação. Ou seja:

!= compara se é diferente

!== compara se não é idêntico

 

Vamos exemplificar:

<?php
$handler = 0;
var_dump($handler != false);// bool(false) 
var_dump($handler !== false);// bool(true)
O que acontece. Ao utilizar o !=, o PHP faz o cast da $handler para booleano, para realizar a comparação. Então, ele entende que 0 é convertido em false, fazendo com que $handler seja considerado igual a false. Já utilizando o !==, o parser entende (ainda bem!!!) que 0 NÃO É idêntico a false, fazendo com que, mesmo que o valor se $handler seja 0.

 

$this->handler =& $handler;

 

=& ta mandando o endereço de memória ou algo assim? (lembrei de linguagem C)

 

Passagem por referência. Como não entendo C mas o core do PHP é em C, então acredito que seja a mesma coisa.

 

Carlos Eduardo

Compartilhar este post


Link para o post
Compartilhar em outros sites

Evando, sobre linkar a library seria ao longo de todo artigo, tal qual o GeSHi faz ao gerar HTML colorido para os códigos? Ou apenas no início ou fim do artigo linkar os recursos utilizados?

Hoje vamos falar sobre IteratorAggregate

 

Certas referências são facilmente encontradas no manual - como a IteratorAggregate, por exemplo - já outras...

 

=& ta mandando o endereço de memória ou algo assim? (lembrei de linguagem C)

Acredito que pelo PHP ser bem escrito e revisto, sim.

 

Mas o propósito não é esse e sim a linkagem de variáveis como um alias

 

$a = 1;
$b =& $a;
$b = 2;
var_dump($a); // (int) 2

Compartilhar este post


Link para o post
Compartilhar em outros sites

- Comentar o código

- Linkar a Library no manual (pra mim é desnecessário, mas tem gente que precisa)

 

Uma vez que a codificação deixou de ser palpável (como "atirei o pau no gato"), alguns termos técnicos acabam carecendo de informações adicionais.

 

Ok, Evandro, não respondi antes justamente por estar reescrevendo todo o material, suas duas sugestões foram totalmente aceitas, obrigado. ;)

 

Principal, mas não exclusivamente, quando há o type hinting de classes ou interfaces que, em uso, significa passa um objeto como argumento de um método, usam-se métodos desse objeto passado o que, na leitura do artigo, se faz necessário "subir e descer" algumas vezes para se completar um raciocínio.

 

Hummm,

 

Bruno, farei o seguinte:

 

1. Usarei a sugestão do Evandro e comentarei o código, método por método. Quando, em algum método, houver o type hinting, comentarei o motivo de tê-lo utilizado.

 

No que !== é diferente de != ?

 

=& ta mandando o endereço de memória ou algo assim? (lembrei de linguagem C)

 

O que acontece. Ao utilizar o !=, o PHP faz o cast da $handler para booleano, para realizar a comparação. Então, ele entende que 0 é convertido em false, fazendo com que $handler seja considerado igual a false. Já utilizando o !==, o parser entende (ainda bem!!!) que 0 NÃO É idêntico a false, fazendo com que, mesmo que o valor se $handler seja 0.

 

Exatamente Matias, perfeito !!! http://forum.imasters.com.br/public/style_emoticons/default/clap.gif

 

Passagem por referência. Como não entendo C mas o core do PHP é em C, então acredito que seja a mesma coisa.

 

Mas o propósito não é esse e sim a linkagem de variáveis como um alias

 

Não seria um apelido, Evandro.

 

Quando utilizamos referências, fazemos exatamente isso, "permitimos que duas variáveis referenciem ao mesmo conteúdo".

 

No manual do PHP existe uma explicação que, ao meu modo de ver, muito boa http://forum.imasters.com.br/public/style_emoticons/default/seta.gif O que as referências fazem ?

 

Se, após a leitura, ainda restarem dúvidas sobre referências, então sugiro que se poste no fórum principal, já que referências estão fora do escopo dos artigos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

/**         * Avança para o próximo elemento         */        public function next(){                $pos = $this->csv->tell();                $this->current = $this->csv->getCSVLine();                ++$this->key;        }
Pq você armazena o valor do tell() no $pos e não usa pra nada depois? você não deveria atribuir o valor do tell() ao key?

 

Dessa forma, o getCSVLine() vai pegar sempre a mesma linha não é?

 

 

me explica ai pq agora eu boiei Imagem Postada

 

 

-----

 

Ah... acho que entendi... o fgets avança as linhas também né...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pq você armazena o valor do tell() no $pos e não usa pra nada depois? você não deveria atribuir o valor do tell() ao key?

Rogégio,

 

O código que postei faz parte de uma biblioteca que venho desenvolvendo já faz um tempo, na hora de postá-lo eu esqueci de remover o tell(); De fato, esse valor não é utilizado e pode ser removido.

 

Dessa forma, o getCSVLine() vai pegar sempre a mesma linha não é?

Absolutamente não, quando se faz a leitura no arquivo, o ponteiro avança. Dessa forma, o getCSVLine() será sempre progressivo.

 

Se utilizássemos o valor do tell() teríamos o offset, em bytes, da posição do ponteiro no arquivo, porém, achei melhor, nesse exemplo, utilizar o key que retornará a linha corrente.

 

 

Ah... acho que entendi... o fgets avança as linhas também né...

Exatamente.

 

Imagem Postada

Compartilhar este post


Link para o post
Compartilhar em outros sites

eu não sei o quão confuso estou mas eu não identifiquei de onde veio isso

 

$this->testOpened()

 

de onde você tirou esse treco?

 

obs: estou relendo mais não consegui identificar ainda.

Compartilhar este post


Link para o post
Compartilhar em outros sites

AbstractFile::testOpened()

 

Como existem diversos tipos de arquivos, as tarefas comuns à eles são abstraídas nessa classe. Dentre essas tarefas está a verificação a Stream foi aberta, atribuindo o resource de fopen() à propriedade AbstractFile::handler

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.