Ir para conteúdo

POWERED BY:

Arquivado

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

Bruno Augusto

Solução para diferença entre arrays multidimensionais...

Recommended Posts

Lembram-se desse problema? Pois então, um amigo me ajudou a fazer uma solução mais limpa e sensata.

 

Mas ela só funciona nos testes, com valores de parâmetro sendo passados manualmente. Quando implemento no sistema "oficial", retorna outra coisa totalmente diferente.

 

Primeiro a solução:

 

function diff( $cache ) {

   return array_udiff( $cache, $this -> data['files'],
                       function( array $cache, array $files ) {

                           return strcmp( $cache['checksum'], $files['checksum'] );
                       }
                     );
}

Óbviamente, um método de classe. array_udiff() computa a diferença do parâmetro $cache com um índice de uma propriedade, pelos checksums.

 

Esta propriedade é:

 

$this -> data = array( 'files' => array( array( 'filepath' => 'C:/Program Files/Zend/Apache2/htdocs/blog/wp-content/themes/Imaggens/admin/application/controllers/AdvertisementController.php',
                                                    'checksum' => '928c5e3bd0c5f8d3613fce7a3bdc9dac',
                                                    //928c5e3bd0c5f8d3613fce7a3bdc9dac - original
                                                    //42ab4fc2af91337be03ce1cb7f4fd837 - alterado
                                                    'filename' => 'AdvertisementController.php',
                                                  ),

                                             array( 'filepath' => 'C:/Program Files/Zend/Apache2/htdocs/blog/wp-content/themes/Imaggens/admin/application/controllers/HomeController.php',
                                                    'checksum' => '28cbf3c60752631a3fa87e427e35afb6',
                                                    'filename' => 'HomeController.php',
                                                  ),

                                              array( 'filepath' => 'C:/Program Files/Zend/Apache2/htdocs/blog/wp-content/themes/Imaggens/admin/library/Zend/Controller/Request/Exception.php',
                                                    'checksum' => 'ef458d8a75cc650d9cbf90ff89df9012',
                                                    'filename' => 'Exception.php',
                                                  ),
                                            ),
                    );

Esse é meu...digamos... Modo Debug, para evitar fazer uma requisição POST só para testar, afinal, por enquanto array_udiff() comparará dois arrays de mais de 400 índices, todo mundo contra todo mundo.

 

Enfim...

 

Se eu invocar o método diff() e passar como argumento esse array:

 

$cache = array( array( 'filepath' => 'wordpress/themes/Corporative/admin/application/controllers/AdvertisementController.php',
                      'checksum' => '928c5e3bd0c5f8d3613fce7a3bdc9dac',
                      'filename' => 'AdvertisementController.php' ),

               array( 'filepath' => 'wordpress/themes/Corporative/admin/application/controllers/BugsController.php',
                      'checksum' => '928ec3fb60a648aeeb62c98408af642c',
                      'filename' => 'BugsController.php' ),
                );

Funciona perfeitamente. Se passo o array REAL, um arquivo de cache com um array serializado, com todos os índices disponíveis, o retorno é todo o array de cache, sem os índices da comparação.

 

Essa última frase ficou um horror de se entender, mas no arquivo anexo ficará bem mais simples de entender.

 

[Anexos - MediaFire]

Classe - teste2.php

Arquivo de Cache - Texto Plano

 

Com o array "manual" fica perfeito. Retorna as informações dos checksums diferentes, mesmo que ele (índice) não exista no segundo array, o que é vital.

Já no segundo não. Pior é que estruturalmente, $cache (manual) e $cache2 (arquivo serializado) são dois array idênticos, salvo pela quantidade de índices.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Imaggens,

 

Faça o teste:

 

IItem.php

interface IItem {
public function compare( IItem $item );
public function getHash();
}

 

IList.php

interface IList extends Iterator, Countable {
public function attach( IItem $item );
public function contains( IItem $item );
public function diff( IList $list );
}

 

AbstractList.php

abstract class AbstractList extends ArrayIterator implements IList {}

 

Item.php

class Item implements IItem {
private $data;
private $hash;

public function __construct( array $item ){
	$this->data = $item;
	$this->hash = md5( implode( '' , $item ) );
}

public function compare( IItem $item ){
	return $this->hash == $item->getHash();
}

public function getHash(){
	return $this->hash;
}
}

 

ItemList.php

class ItemList extends AbstractList {
public function attach( IItem $item ){
	$this[ $item->getHash() ] = $item;
}

public function contains( IItem $item ){
	return isset( $this[ $item->getHash() ] );
}

public static function createFromArray( array $input ){
	$ret = new ItemList();

	foreach ( $input as $item ){
		$ret->attach( new Item( $item ) );
	}

	return $ret;
}

public function diff( IList $list ){
	$ret = new ItemList();
	$lsa = $this;
	$lsb = $list;

	if ( $list->count() > $this->count() ){
		$lsa = $list;
		$lsb = $this;
	}

	foreach ( $lsa as $item ){
		if ( !$lsb->contains( $item ) ){
			$ret->attach( $item );
		}
	}

	return $ret;
}
}

 

test.php

$cache = unserialize( file_get_contents( 'cache.md5' ) );

$list = ItemList::createFromArray( $cache );

$cache[ 3 ][ 'checksum' ] = '928ec3fb60a648aeeb62c98408af6424';

array_pop( $cache );

var_dump( $list->diff( ItemList::createFromArray( $cache ) ) );

 

Saída:

object(ItemList)#946 (1) {
 ["storage":"ArrayIterator":private]=>
 array(2) {
	["89d079731b7eec33864014b11ea16d6e"]=>
	object(Item)#5 (2) {
 	["data":"Item":private]=>
 	array(3) {
 	["filepath"]=>
 	string(81) "wordpress/themes/Corporative/admin/application/controllers/DatabaseController.php"
 	["checksum"]=>
 	string(32) "4846aa17f662a8d65e4901d7597d5a37"
 	["filename"]=>
 	string(22) "DatabaseController.php"
 	}
 	["hash":"Item":private]=>
 	string(32) "89d079731b7eec33864014b11ea16d6e"
	}
	["dde60a2a143480d8917310ff7ab4efe5"]=>
	object(Item)#473 (2) {
 	["data":"Item":private]=>
 	array(3) {
 	["filepath"]=>
 	string(38) "wordpress/themes/Corporative/style.css"
 	["checksum"]=>
 	string(32) "3e4bfc4efe89d2f6615c4d6c2c417b8b"
 	["filename"]=>
 	string(9) "style.css"
 	}
 	["hash":"Item":private]=>
 	string(32) "dde60a2a143480d8917310ff7ab4efe5"
	}
 }
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

:o

 

Testei e obtive o mesmo resultado que você, mas devido a ausência de comentários, o nunca obtido contato com Storage e a quantidade limitada de Interfaces que uso, não entendi muita coisa.

 

Fui comentando os métodos, pelo menos o que entendi. Se puder explicar o que acertei e o que errei nesses comentários para ilustrar melhor a solução, agradeço:

 

Item.php

<?php

require_once 'IItem.php';

class Item implements IItem {
        private $data;
        private $hash;

        /**
         * Construtor
         *
         * A propriedade $data com o array de parâmetro e
         * a propriedade $hash com o MD5 de todos os índices
         * do array de parâmetro.
         *
         * Mas para quê?
         */
        public function __construct( array $item ){
                $this->data = $item;
                $this->hash = md5( implode( '' , $item ) );
        }

        /**
         * Compara o valor da propriedade $hash com... ela mesma?
         *
         * Esse método é usado onde?
         */
        public function compare( IItem $item ){
                return $this->hash == $item->getHash();
        }

        /**
         * Retorna a propriedade $hash
         */
        public function getHash(){
                return $this->hash;
        }
}

ItemList

<?php

require_once 'AbstractList.php';
require_once 'Item.php';

class ItemList extends AbstractList {
        /**
         * Adiciona o objeto de IItem, construído por Item.
         *
         * Mas adiciona onde? Por estender ArrayIterator, $this
         * passa a ser interpretado como array?
         */
        public function attach( IItem $item ){
                $this[ $item->getHash() ] = $item;
        }

        /**
         * Verifica se o hash do objeto IItem, construído por Item,
         * existe no array $this (vale a mesma pergunta do comentário anterior)
         */
        public function contains( IItem $item ){
                return isset( $this[ $item->getHash() ] );
        }

        /**
         * Percorre o array de parâmetro, adicionando ao $this, via
         * attach(), novas instâncias do objeto Item
         */
        public static function createFromArray( array $input ){
                $ret = new ItemList();

                foreach ( $input as $item ){
                        $ret->attach( new Item( $item ) );
                }

                return $ret;
        }

        /**
         * Computa a diferença
         */
        public function diff( IList $list ){
                /**
                 * Instancia o objeto ItemList
                 *
                 * Isso é porque, devido ao ArrayIterator fazer de
                 * $this um array, em decorrência de attach(), ele ($this)
                 * já está preenchido com os vários objetos a partir
                 * dos índices do array, enquanto se faz necessário
                 * de um objeto vazio?
                 */
                $ret = new ItemList();
                
                // Associa $this (preenchido) à uma variável
                $lsa = $this;
                
                /**
                 * Associa o parâmetro à uma variável
                 *
                 * Mas $list é uma instância dessa própria classe
                 * e, nos testes, é o retorno de createFromArray(),
                 * então, está todo preenchido.
                 *
                 * Não é mesmo que o $this, transmutado em array por ArrayIterator?
                 */
                $lsb = $list;

                // Conta os elementos do objeto de parâmetro com os existentes em $this...
                if ( $list->count() > $this->count() ){
                
                        /**
                         *  ... se or maior, reassocia as variáveis
                         *
                         *  $lsa, se transforma em $lsb ($list)
                         *  $lsb, se transforma em $las ($this)
                         *
                         *  Pra quê isso?
                         */
                        $lsa = $list;
                        $lsb = $this;
                }

                /**
                 * Percorre $lsa
                 *
                 *  Por causa da "troca" acima, eu me perdi :( 
                 */
                foreach ( $lsa as $item ){
                        /**
                         * Se o objeto não exisitir no storage, adiciona-o, via attach()
                         *
                         * SplObjectStorage não é uma classe? Quando ela é construída?
                         */
                        if ( !$lsb->contains( $item ) ){
                                $ret->attach( $item );
                        }
                }

                return $ret;
        }
}
As Interfaces e a classe Abstrata nem mexi.

 

Testar eu testei e, apesar de não ter entendido tudo, tentei implementar no mesmo esquema de arrays proposto no início do tópico.

 

Fiz assim:

 

<?php

require_once 'ItemList.php';

$cache = unserialize( file_get_contents( 'cache.md5' ) );

$list = ItemList::createFromArray( $cache );

$files = array( 'files' => array( array( 'filepath' => 'admin/application/controllers/AdvertisementController.php',
                                         'checksum' => '928c5e3bd0c5f8d3613fce7a3bdc9dac',
                                         //928c5e3bd0c5f8d3613fce7a3bdc9dac - original
                                         //42ab4fc2af91337be03ce1cb7f4fd837 - alterado
                                         'filename' => 'AdvertisementController.php',
                                       ),

                                  array( 'filepath' => 'admin/application/controllers/HomeController.php',
                                         'checksum' => '28cbf3c60752631a3fa87e427e35afb6',
                                         'filename' => 'HomeController.php',
                                       ),

                                  array( 'filepath' => 'admin/library/Zend/Controller/Request/Exception.php',
                                         'checksum' => 'ef458d8a75cc650d9cbf90ff89df9012',
                                         'filename' => 'Exception.php',
                                       ),
                                ),
              );

print '<pre>'; print_r( $list->diff( ItemList::createFromArray( $files['files'] ) ) ); print '</pre>';
O uso do índice da variável foi para melhor se assemelhar ao sistema real, onde esse array se encontra em uma propriedade multidimensional.

 

O resultado foi, pelo menos aparentemente, todo o array de cache convertidos para o esquema de objetos.

 

Pergunta: Esse sistema considera como diferença apenas os hashes né? Porque em ambos os arrays os filepaths são diferentes (o cache possui três níveis "a mais").

Compartilhar este post


Link para o post
Compartilhar em outros sites

O resultado foi, pelo menos aparentemente, todo o array de cache convertidos para o esquema de objetos.

 

Pergunta: Esse sistema considera como diferença apenas os hashes né? Porque em ambos os arrays os filepaths são diferentes (o cache possui três níveis "a mais").

 

Certo,

 

Então antes de comentar o código, enumere o que deve ser levado em consideração na comparação e o que não deve

Compartilhar este post


Link para o post
Compartilhar em outros sites

Inicialmente, a intenção era comparar os índices filepath e verificar se, entre si, haviam diferenças no s índices checksum.

 

No link passado no tópico de início, me foi mostrado um esquema ao qual já tinha chegado com dois loop's aninhados. Eu achei muito lento, mas às ve4zes não funcionava.

 

Essa segunda solução, pelo que me foi dito, compara "todo mundo contra todo mundo" e que, mesmo assim, era bem rápido. Eu não via como, afinal são, por enquanto, 472x472 (quantidade de arquivos).

 

Porém eu esbarrei num probleminha com essa solução que foi que eu não poderia JAMAIS até mesmo esquecer de remover algum arquivo que potencialmente tenha o mesmo checksum, o que às vezes pode ocorrer quando se envia algo a mais para o FTP sem querer.

 

Com isso, preciso enviar via POST com cURL/Stream Context essas estrutura, dos 400 e tantos arquivos da instalação corrente do sistema que meu usuário possuir e comparar com o array serialziado armazenado em arquivo para obter as diferenças.

 

Depois eu transformo em XML para manipular via serviço, mas isso não vem ao caso.

 

Essa comparação deve ser hábil o suficiente para retornar os índices do cache que tiverem checksum diferente dos enviados por POST (atualmente, manual, como debug) e também, incluir nessa lista de diferenças os índices que NÃO se encontram no array enviado, caracterizando algum novo recurso que implentou X arquivos novos.

 

E, como extra, se puder fazer de forma mais rápida do que comparando "todo mundo contra todo mundo", para que o serviço seja mais rápido.

 

Resumindo? Receber um array bidimensional de X índices, comparar com o serializado do arquivos de Y índices e retornam as diferenças de checksum por arquivo ou filepath.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Inicialmente, a intenção era comparar os índices filepath e verificar se, entre si, haviam diferenças no s índices checksum.

 

Resumindo? Receber um array bidimensional de X índices, comparar com o serializado do arquivos de Y índices e retornam as diferenças de checksum por arquivo ou filepath.

 

Bom, fazendo a comparação pelo filepath, vou postar apenas as modificações para economizar espaço, o resto do código é exatamente o mesmo:

 

IItem.php

interface IItem {
public function compare( IItem $item );
public function getChecksum(); //Método adicionado
public function getHash();
}

 

Item.php

class Item implements IItem {
private $data;
private $hash;

public function __construct( array $item ){
	$this->data = $item;
	$this->hash = md5( $item[ 'filename' ] ); //md5( $item[ 'filepath' ] );
}

public function compare( IItem $item ){
	return ( $this->hash == $item->getHash() ) && ( $item->getChecksum() == $this->data[ 'checksum' ] );
}

public function getChecksum(){
	return $this->data[ 'checksum' ];
}

//...
}

 

ItemList.php

class ItemList extends AbstractList {
//...
public function contains( IItem $item ){
	$hash = $item->getHash();

	return isset( $this[ $hash ] ) && ( $this[ $hash ]->compare( $item ) );
}
//...
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Perfeito :D

 

Lista os arquivos enviados que possuem hashes diferentes dos do cache e ainda inclui nessa diferença os arquivos não enviados, para o caso de uma atualização incluir um feature que implemente outro arquivo.

 

Mas me ocorreu uma idéia para acelerar, pelo menos um pouquinho, e diminuir o consumo de banda do servidor (quando online).

 

Ao invés de comparar os 472 arquivos contra os 472 arquivos, comparar essa quantia vinda do servidor do usuário com um cache reduzido, contendo apenas os arquivos modificados.

 

Testei o cache completo, anotei as ocorrências encontradas e recriei o cache em cima desses arquivos apenas, vindos de outro diretório como repositório.

 

Com isso, obtive um arquivo sensivelmente menor (obviamente), mas a comparação deixou de funcionar porque, nesse modelo, vários índices não modificados vindos via POST (array de comparação), não existem no cache e, portanto, estão sendo incluídos na diferença.

 

É possível fazer essa adaptação para tornar o sistema mais leve e, de quebra, mais seguro, já que não teria uma cópia de toda a instalação do sistema no meu servidor?

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.