paths = $paths; $this->filter = $filter; $this->root = '' !== $root ? Str::untrailingslash( Fs::normalize( $root ) ) : Str::untrailingslash( $paths->root() ); } /** * Fresh scanning state. * * @return array */ public static function initial_state(): array { return array( 'stack' => array( '' ), 'files' => 0, 'bytes' => 0, 'directories' => 0, 'skipped' => 0, 'done' => false, ); } /** * Scan for at most `$deadline`. * * @param array $state Mutable scanning state. * @param Deadline $deadline Time budget. * @param callable $on_file Callback receiving a {@see FileEntry}. * @return array Updated state. */ public function scan( array $state, Deadline $deadline, callable $on_file ): array { $state = array_merge( self::initial_state(), $state ); while ( true ) { if ( array() === $state['stack'] ) { $state['done'] = true; break; } if ( $deadline->expired() ) { break; } $relative_dir = (string) array_pop( $state['stack'] ); $absolute_dir = '' === $relative_dir ? $this->root : $this->root . '/' . $relative_dir; // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $items = @scandir( $absolute_dir ); if ( false === $items ) { ++$state['skipped']; continue; } ++$state['directories']; foreach ( $items as $item ) { if ( '.' === $item || '..' === $item ) { continue; } $item_relative = '' === $relative_dir ? $item : $relative_dir . '/' . $item; $item_absolute = $absolute_dir . '/' . $item; if ( is_link( $item_absolute ) ) { ++$state['skipped']; continue; } if ( is_dir( $item_absolute ) ) { if ( $this->paths->contains( $item_absolute ) ) { ++$state['skipped']; continue; } if ( $this->filter->allows_dir( $item_relative ) ) { $state['stack'][] = $item_relative; } else { ++$state['skipped']; } continue; } // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $size = (int) @filesize( $item_absolute ); if ( ! $this->filter->allows_file( $item_relative, $size ) ) { ++$state['skipped']; continue; } call_user_func( $on_file, new FileEntry( $item_absolute, $item_relative, $size ) ); ++$state['files']; $state['bytes'] += $size; } } return $state; } }