Huymada

Washing

May 26th, 2022 (edited)
1,511
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 6.12 KB | None | 0 0
  1. <?php
  2. /**
  3.  * Washing - Remove arquivos e diretórios recursivamente
  4.  * @version 2.0
  5.  */
  6. class Washing {
  7.  
  8.     /**
  9.      * Caminhos críticos bloqueados por segurança
  10.      * Chamadas que apontem para esses diretórios lançam exceção
  11.      */
  12.     private array $blocked = ['/', '/etc', '/var', '/usr', '/bin', '/boot'];
  13.  
  14.     /**
  15.      * Relatório da última operação
  16.      * ['removed' => [...], 'failed' => [...]]
  17.      */
  18.     private array $report = ['removed' => [], 'failed' => []];
  19.  
  20.     // -------------------------------------------------------------------------
  21.  
  22.     /**
  23.      * Aceita qualquer combinação de strings e arrays com caminhos
  24.      * Se chamado sem argumentos, limpa o diretório atual
  25.      *
  26.      * @example
  27.      *   new Washing();                          // limpa getcwd()
  28.      *   new Washing('/tmp/cache');              // limpa um diretório
  29.      *   new Washing('/tmp/cache', '/tmp/logs'); // limpa múltiplos
  30.      *   new Washing(['/tmp/a', '/tmp/b']);      // aceita array também
  31.      */
  32.     public function __construct(mixed ...$items) {
  33.         $this->report = ['removed' => [], 'failed' => []];
  34.  
  35.         if (empty($items)) {
  36.             $this->process(getcwd());
  37.             return;
  38.         }
  39.  
  40.         foreach ($items as $item) {
  41.             $this->resolve($item);
  42.         }
  43.     }
  44.  
  45.     // -------------------------------------------------------------------------
  46.  
  47.     /**
  48.      * Resolve o tipo do argumento (string ou array) antes de limpar
  49.      */
  50.     private function resolve(mixed $item): void {
  51.         if (is_array($item)) {
  52.             foreach ($item as $path) {
  53.                 $this->resolve($path);
  54.             }
  55.             return;
  56.         }
  57.  
  58.         if (is_string($item) && $item !== '') {
  59.             $this->process($item);
  60.             return;
  61.         }
  62.  
  63.         // Argumento inválido — limpa diretório atual como fallback
  64.         $this->process(getcwd());
  65.     }
  66.  
  67.     /**
  68.      * Executa a remoção de um arquivo ou diretório
  69.      *
  70.      * @throws InvalidArgumentException se o caminho for bloqueado
  71.      * @throws RuntimeException         se o caminho não existir
  72.      */
  73.     private function process(string $path): void {
  74.         $real = realpath($path);
  75.  
  76.         // Caminho não existe
  77.         if ($real === false) {
  78.             throw new RuntimeException(
  79.                 "Washing: caminho não encontrado — \"{$path}\""
  80.             );
  81.         }
  82.  
  83.         // Bloqueio de caminhos críticos
  84.         foreach ($this->blocked as $blocked) {
  85.             if ($real === $blocked || str_starts_with($real, $blocked . DIRECTORY_SEPARATOR)) {
  86.                 throw new InvalidArgumentException(
  87.                     "Washing: caminho bloqueado por segurança — \"{$real}\""
  88.                 );
  89.             }
  90.         }
  91.  
  92.         if (is_file($real)) {
  93.             $this->removeFile($real);
  94.             return;
  95.         }
  96.  
  97.         if (is_dir($real)) {
  98.             $this->removeDir($real);
  99.             return;
  100.         }
  101.  
  102.         $this->report['failed'][] = $real;
  103.     }
  104.  
  105.     /**
  106.      * Remove um arquivo único
  107.      */
  108.     private function removeFile(string $path): void {
  109.         if (unlink($path)) {
  110.             $this->report['removed'][] = $path;
  111.         } else {
  112.             $this->report['failed'][] = $path;
  113.         }
  114.     }
  115.  
  116.     /**
  117.      * Remove um diretório recursivamente (conteúdo + o próprio diretório)
  118.      */
  119.     private function removeDir(string $path): void {
  120.         $iterator = new RecursiveIteratorIterator(
  121.             new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
  122.             RecursiveIteratorIterator::CHILD_FIRST
  123.         );
  124.  
  125.         foreach ($iterator as $file) {
  126.             $filepath = $file->getPathname();
  127.  
  128.             if ($file->isFile() || $file->isLink()) {
  129.                 if (unlink($filepath)) {
  130.                     $this->report['removed'][] = $filepath;
  131.                 } else {
  132.                     $this->report['failed'][] = $filepath;
  133.                 }
  134.             } elseif ($file->isDir()) {
  135.                 if (rmdir($filepath)) {
  136.                     $this->report['removed'][] = $filepath;
  137.                 } else {
  138.                     $this->report['failed'][] = $filepath;
  139.                 }
  140.             }
  141.         }
  142.  
  143.         // Remove o diretório raiz após esvaziar
  144.         if (rmdir($path)) {
  145.             $this->report['removed'][] = $path;
  146.         } else {
  147.             $this->report['failed'][] = $path;
  148.         }
  149.     }
  150.  
  151.     // -------------------------------------------------------------------------
  152.     // RELATÓRIO
  153.     // -------------------------------------------------------------------------
  154.  
  155.     /**
  156.      * Retorna o relatório completo da última operação
  157.      *
  158.      * @return array{removed: string[], failed: string[]}
  159.      *
  160.      * @example
  161.      *   $w = new Washing('/tmp/cache');
  162.      *   $report = $w->getReport();
  163.      *   echo count($report['removed']) . " removidos";
  164.      *   echo count($report['failed'])  . " falhas";
  165.      */
  166.     public function getReport(): array {
  167.         return $this->report;
  168.     }
  169.  
  170.     /**
  171.      * Retorna true se a operação não teve nenhuma falha
  172.      */
  173.     public function success(): bool {
  174.         return empty($this->report['failed']);
  175.     }
  176.  
  177.     /**
  178.      * Retorna a lista de itens removidos com sucesso
  179.      */
  180.     public function removed(): array {
  181.         return $this->report['removed'];
  182.     }
  183.  
  184.     /**
  185.      * Retorna a lista de itens que falharam
  186.      */
  187.     public function failed(): array {
  188.         return $this->report['failed'];
  189.     }
  190.  
  191.     // -------------------------------------------------------------------------
  192.     // CONFIGURAÇÃO
  193.     // -------------------------------------------------------------------------
  194.  
  195.     /**
  196.      * Adiciona um caminho à lista de bloqueados
  197.      *
  198.      * @example
  199.      *   $w = new Washing();
  200.      *   $w->addBlocked('/var/www/producao');
  201.      */
  202.     public function addBlocked(string $path): void {
  203.         $real = realpath($path);
  204.         if ($real && !in_array($real, $this->blocked)) {
  205.             $this->blocked[] = $real;
  206.         }
  207.     }
  208.  
  209. }
Advertisement
Add Comment
Please, Sign In to add comment