public static function get_tree(string $dir, bool $only_dir = false, callable $filter = null, bool $hidden = false): array
  {
    $r = [];
    $dir = self::clean($dir);
    $dirs = self::get_dirs($dir, $hidden);
    if ( \is_array($dirs) ){
      foreach ( $dirs as $d ){
        $x = [
          'name' => $d,
          'type' => 'dir',
          'num_children' => 0,
          'items' => self::get_tree($d, $only_dir, $filter, $hidden)
        ];
        $x['num_children'] = \count($x['items']);
        if ( $filter ){
          if ( $filter($x) ){
            $r[] = $x;
          }
        }
        else{
          $r[] = $x;
        }
      }
      if ( !$only_dir ){
        $files = self::get_files($dir, false, $hidden);
        foreach ( $files as $f ){
          $x = [
            'name' => $f,
            'type' => 'file',
            'ext' => bbn\str::file_ext($f)
          ];
          if ( $filter ){
            if ( $filter($x) ){
              $r[] = $x;
            }
          }
          else{
            $r[] = $x;
          }
        }
      }
    }
    return $r;
  }