src/PeskyCMF/ApiDocs/CmfApiDocumentationModule.php
protected function loadClassesFromFileSystem(): array {
        $rootFolderPath = $this->getClassesFolderPath();
        $folder = Folder::load($rootFolderPath);
        if (!$folder->exists()) {
            return [];
        }
        $ret = [];
        $classFinder = function ($folderPath, array $files) {
            $classes = [];
            foreach ($files as $fileName) {
                if (preg_match('%\.php$%i', $fileName)) {
                    $file = fopen($folderPath . DIRECTORY_SEPARATOR . $fileName, 'rb');
                    $buffer = fread($file, 512);
                    $parentClassName = class_basename($this->getMethodBaseClass()) . '|[a-zA-Z0-9_-]+ApiMethodDocumentation|CmfApiDocumentation';
                    if (preg_match('%^\s*class\s+(\w+)\s+extends\s+(' . $parentClassName . ')%im', $buffer, $classMatches)) {
                        $class = $classMatches[1];
                        if (preg_match("%[^w]namespace\s+([\w\\\]+).*?class\s+{$class}\s+%is", $buffer, $nsMatches)) {
                            $namespace = $nsMatches[1];
                            $classes[] = '\\' . $namespace . '\\' . $class;
                        }
                    }
                }
            }
            // sort classes
            usort($classes, function ($class1, $class2) {
                /** @var CmfApiDocumentation $class1 */
                /** @var CmfApiDocumentation $class2 */
                $pos1 = $class1::getPosition();
                $pos2 = $class2::getPosition();
                if ($pos1 === null) {
                    return $pos2 === null ? 0 : 1;
                } else if ($pos2 === null) {
                    return $pos1 === null ? 0 : -1;
                } else if ($pos1 === $pos2) {
                    return 0;
                } else {
                    return $pos1 > $pos2;
                }
            });
            return $classes;
        };
        list($subFolders, $files) = $folder->read();
        $withoutSection = $classFinder($folder->pwd(), $files);
        if (!empty($withoutSection)) {
            $ret[(string)$this->getCmfConfig()->transCustom('api_docs.section.no_section')] = $withoutSection;
        }
        foreach ($subFolders as $subFolderName) {
            if ($subFolderName[0] === '.') {
                // ignore folders starting with '.' - nothing useful there
                continue;
            }
            $subFolder = Folder::load($folder->pwd() . DIRECTORY_SEPARATOR . $subFolderName);
            $files = $subFolder->find('.*\.php');
            $classes = $classFinder($subFolder->pwd(), $files);
            if (!empty($classes)) {
                $ret[(string)$this->getCmfConfig()->transApiDoc('section.' . snake_case($subFolderName))] = $classes;
            }
        }
        return $ret;
    }
protected function loadClassesFromFileSystem(): array {
        $rootFolderPath = $this->getClassesFolderPath();
        $folder = Folder::load($rootFolderPath);
        if (!$folder->exists()) {
            return [];
        }
        $ret = [];
        $classFinder = function ($folderPath, array $files) {
            $classes = [];
            foreach ($files as $fileName) {
                if (preg_match('%\.php$%i', $fileName)) {
                    $file = fopen($folderPath . DIRECTORY_SEPARATOR . $fileName, 'rb');
                    $buffer = fread($file, 512);
                    $parentClassName = class_basename($this->getMethodBaseClass()) . '|[a-zA-Z0-9_-]+ApiMethodDocumentation|CmfApiDocumentation';
                    if (preg_match('%^\s*class\s+(\w+)\s+extends\s+(' . $parentClassName . ')%im', $buffer, $classMatches)) {
                        $class = $classMatches[1];
                        if (preg_match("%[^w]namespace\s+([\w\\\]+).*?class\s+{$class}\s+%is", $buffer, $nsMatches)) {
                            $namespace = $nsMatches[1];
                            $classes[] = '\\' . $namespace . '\\' . $class;
                        }
                    }
                }
            }
            // sort classes
            usort($classes, function ($class1, $class2) {
                /** @var CmfApiDocumentation $class1 */
                /** @var CmfApiDocumentation $class2 */
                $pos1 = $class1::getPosition();
                $pos2 = $class2::getPosition();
                if ($pos1 === null) {
                    return $pos2 === null ? 0 : 1;
                } else if ($pos2 === null) {
                    return $pos1 === null ? 0 : -1;
                } else if ($pos1 === $pos2) {
                    return 0;
                } else {
                    return $pos1 > $pos2;
                }
            });
            return $classes;
        };
        list($subFolders, $files) = $folder->read();
        $withoutSection = $classFinder($folder->pwd(), $files);
        if (!empty($withoutSection)) {
            $ret[(string)$this->getCmfConfig()->transCustom('api_docs.section.no_section')] = $withoutSection;
        }
        foreach ($subFolders as $subFolderName) {
            if ($subFolderName[0] === '.') {
                // ignore folders starting with '.' - nothing useful there
                continue;
            }
            $subFolder = Folder::load($folder->pwd() . DIRECTORY_SEPARATOR . $subFolderName);
            $files = $subFolder->find('.*\.php');
            $classes = $classFinder($subFolder->pwd(), $files);
            if (!empty($classes)) {
                $ret[(string)$this->getCmfConfig()->transApiDoc('section.' . snake_case($subFolderName))] = $classes;
            }
        }
        return $ret;
    }
Load api dosc sections from files in static::api_methods_documentation_classes_folder() and its subfolders.
Should be used only when static::config('api_docs_class_names') not provided.
Subfolders names used as API sections.
Collects only classes that extend next classes:
- ApiDocumentation
- ApiMethodDocumentation
- static::api_method_documentation_base_class()
@return array