Public Access
b491241a58
Universal: Sync Feature Branch Versions / Sync feature branches with dev (push) Has been skipped
# Conflicts: # .mokogitea/CLAUDE.md # .mokogitea/ISSUE_TEMPLATE/config.yml # .mokogitea/ISSUE_TEMPLATE/documentation.md # .mokogitea/ISSUE_TEMPLATE/feature_request.md # .mokogitea/ISSUE_TEMPLATE/security.md # .mokogitea/branch-protection.yml # .mokogitea/bulk-repo-sync.yml # .mokogitea/pr-branch-check.yml # .mokogitea/renovate.yml # .mokogitea/sync-wikis.yml # .mokogitea/workflows/auto-bump.yml # .mokogitea/workflows/auto-release.yml # .mokogitea/workflows/ci-platform.yml # .mokogitea/workflows/cleanup.yml # .mokogitea/workflows/gitleaks.yml # .mokogitea/workflows/issue-branch.yml # .mokogitea/workflows/notify.yml # .mokogitea/workflows/pre-release.yml # .mokogitea/workflows/repo-health.yml # .mokogitea/workflows/security-audit.yml # .script-registry.json # CHANGELOG.md # PLUGIN_SCRIPTS.md # README.md # analysis/index.md # automation/bulk_joomla_template.php # automation/bulk_sync.php # automation/enrich_manifest_xml.php # automation/enrich_mokostandards_xml.php # automation/index.md # automation/migrate_to_gitea.php # automation/push_files.php # automation/push_manifest_xml.php # automation/push_mokostandards_xml.php # automation/repo_cleanup.php # bin/moko # cli/archive_repo.php # cli/audit_query.php # cli/badge_update.php # cli/branch_rename.php # cli/bulk_workflow_push.php # cli/bulk_workflow_trigger.php # cli/changelog_promote.php # cli/changelog_prune.php # cli/client_dashboard.php # cli/client_health_check.php # cli/client_inventory.php # cli/client_provision.php # cli/completion.php # cli/create_project.php # cli/create_repo.php # cli/deploy_joomla.php # cli/dev_branch_reset.php # cli/grafana_dashboard.php # cli/joomla_build.php # cli/joomla_compat_check.php # cli/joomla_metadata_validate.php # cli/joomla_release.php # cli/license_manage.php # cli/manifest_element.php # cli/manifest_licensing.php # cli/manifest_read.php # cli/package_build.php # cli/platform_detect.php # cli/release.php # cli/release_body_update.php # cli/release_cascade.php # cli/release_create.php # cli/release_manage.php # cli/release_mirror.php # cli/release_notes.php # cli/release_package.php # cli/release_promote.php # cli/release_publish.php # cli/release_validate.php # cli/release_verify.php # cli/scaffold_client.php # cli/sync_rulesets.php # cli/theme_lint.php # cli/updates_xml_build.php # cli/updates_xml_sync.php # cli/version_auto_bump.php # cli/version_bump.php # cli/version_bump_remote.php # cli/version_check.php # cli/version_read.php # cli/version_reset_dev.php # cli/version_set_platform.php # cli/wiki_sync.php # cli/workflow_sync.php # composer.json # deploy/backup-before-deploy.php # deploy/deploy-dolibarr.php # deploy/deploy-joomla.php # deploy/deploy-sftp.php # deploy/health-check.php # deploy/rollback-joomla.php # deploy/sync-joomla.php # fix/fix_line_endings.php # fix/fix_permissions.php # fix/fix_tabs.php # fix/fix_trailing_spaces.php # fix/index.md # index.md # lib/CliBase.php # lib/Common.php # lib/Enterprise/AbstractProjectPlugin.php # lib/Enterprise/ApiClient.php # lib/Enterprise/AuditLogger.php # lib/Enterprise/CheckpointManager.php # lib/Enterprise/CliFramework.php # lib/Enterprise/Config.php # lib/Enterprise/ConfigValidator.php # lib/Enterprise/EnterpriseReadinessValidator.php # lib/Enterprise/ErrorRecovery.php # lib/Enterprise/FileFixUtility.php # lib/Enterprise/GitHubAdapter.php # lib/Enterprise/GitPlatformAdapter.php # lib/Enterprise/InputValidator.php # lib/Enterprise/ManifestParser.php # lib/Enterprise/ManifestReader.php # lib/Enterprise/MetricsCollector.php # lib/Enterprise/MokoGiteaAdapter.php # lib/Enterprise/PackageBuilder.php # lib/Enterprise/PlatformAdapterFactory.php # lib/Enterprise/PluginFactory.php # lib/Enterprise/PluginRegistry.php # lib/Enterprise/Plugins/ApiPlugin.php # lib/Enterprise/Plugins/DocumentationPlugin.php # lib/Enterprise/Plugins/DolibarrPlugin.php # lib/Enterprise/Plugins/GenericPlugin.php # lib/Enterprise/Plugins/JoomlaPlugin.php # lib/Enterprise/Plugins/McpServerPlugin.php # lib/Enterprise/Plugins/MobilePlugin.php # lib/Enterprise/Plugins/NodeJsPlugin.php # lib/Enterprise/Plugins/PythonPlugin.php # lib/Enterprise/Plugins/TerraformPlugin.php # lib/Enterprise/Plugins/WordPressPlugin.php # lib/Enterprise/ProjectConfigValidator.php # lib/Enterprise/ProjectMetricsCollector.php # lib/Enterprise/ProjectPluginInterface.php # lib/Enterprise/ProjectTypeDetector.php # lib/Enterprise/RecoveryError.php # lib/Enterprise/RecoveryManager.php # lib/Enterprise/RepositoryHealthChecker.php # lib/Enterprise/RepositorySynchronizer.php # lib/Enterprise/RetryHelper.php # lib/Enterprise/SecurityValidator.php # lib/Enterprise/SourceResolver.php # lib/Enterprise/SynchronizationException.php # lib/Enterprise/TransactionManager.php # lib/Enterprise/UnifiedValidation.php # lib/index.md # lib/plugins/Joomla/UpdateXmlGenerator.php # maintenance/index.md # maintenance/pin_action_shas.php # maintenance/repo_inventory.php # maintenance/rotate_secrets.php # maintenance/setup_labels.php # maintenance/sync_dolibarr_readmes.php # maintenance/update_repo_inventory.php # maintenance/update_sha_hashes.php # maintenance/update_version_from_readme.php # mcp/config.example.json # mcp/package.json # mcp/src/config.ts # mcp/src/index.ts # mcp/src/runner.ts # mcp/src/types.ts # phpcs.xml # plugin_health_check.php # plugin_list.php # plugin_metrics.php # plugin_readiness.php # plugin_validate.php # release/generate_dolibarr_version_txt.php # release/generate_joomla_update_xml.php # src/functions.php # templates/configs/README.md # templates/configs/index.md # templates/configs/manifest.xml.template # templates/configs/manifest.yml.template # templates/configs/mokostandards.xml.template # templates/configs/mokostandards.yml.template # templates/configs/phpcs.xml # templates/docs/README.md # templates/docs/extra/README.md # templates/docs/extra/index.md # templates/docs/index.md # templates/docs/required/GOVERNANCE.md # templates/docs/required/README.md # templates/docs/required/index.md # templates/docs/required/template-CONTRIBUTING.md # templates/docs/required/template-README.md # templates/docs/required/template-SECURITY.md # templates/index.md # templates/licenses/README.md # templates/licenses/index.md # templates/makefiles/README.md # templates/mokogitea/CLAUDE.dolibarr.md.template # templates/mokogitea/CLAUDE.joomla.md.template # templates/mokogitea/CLAUDE.md.template # templates/mokogitea/ISSUE_TEMPLATE/config.yml # templates/mokogitea/ISSUE_TEMPLATE/documentation.md # templates/mokogitea/ISSUE_TEMPLATE/dolibarr_module_id_request.md # templates/mokogitea/ISSUE_TEMPLATE/feature_request.md # templates/mokogitea/ISSUE_TEMPLATE/security.md # templates/mokogitea/README.md # templates/mokogitea/copilot-instructions.dolibarr.md.template # templates/mokogitea/copilot-instructions.joomla.md.template # templates/mokogitea/copilot-instructions.md.template # templates/mokogitea/dependabot.yml.template # templates/mokogitea/override.tf.template # templates/required/README.md # templates/schemas/README.md # templates/schemas/manifest-schema.xsd # templates/schemas/moko-platform-schema.xsd # templates/schemas/mokostandards-schema.xsd # templates/schemas/schemas/README.md # templates/schemas/template-repository-structure.xml # templates/scripts/README.md # templates/scripts/common/CliBase.template.php # templates/scripts/fix/index.md # templates/scripts/index.md # templates/scripts/release/index.md # templates/scripts/release/package_dolibarr.php # templates/scripts/release/package_joomla.php # templates/scripts/sftp-config/README.md # templates/scripts/validate/dolibarr_module.php # templates/scripts/validate/index.md # templates/scripts/validate/validate_manifest.php # templates/scripts/validate/validate_structure.php # templates/security/README.md # templates/security/index.php # templates/stubs/dolibarr.php # templates/stubs/joomla.php # templates/web/index.php # tests/Enterprise/GitPlatformAdapterTest.php # tests/Unit/VersionBumpTest.php # tests/Unit/VersionReadTest.php # tests/index.md # tests/test_circuit_breaker_handling.php # tests/test_enterprise_libraries.php # validate/SECURITY_SCANNING.md # validate/auto_detect_platform.php # validate/check_changelog.php # validate/check_client_theme.php # validate/check_composer_deps.php # validate/check_dolibarr_module.php # validate/check_enterprise_readiness.php # validate/check_file_integrity.php # validate/check_joomla_manifest.php # validate/check_language_structure.php # validate/check_license_headers.php # validate/check_no_secrets.php # validate/check_paths.php # validate/check_php_syntax.php # validate/check_repo_health.php # validate/check_structure.php # validate/check_tabs.php # validate/check_version_consistency.php # validate/check_wiki_health.php # validate/check_xml_wellformed.php # validate/index.md # validate/scan_drift.php # wrappers/auto_detect_platform.php # wrappers/bulk_sync.php # wrappers/check_changelog.php # wrappers/check_dolibarr_module.php # wrappers/check_enterprise_readiness.php # wrappers/check_joomla_manifest.php # wrappers/check_language_structure.php # wrappers/check_license_headers.php # wrappers/check_no_secrets.php # wrappers/check_paths.php # wrappers/check_php_syntax.php # wrappers/check_repo_health.php # wrappers/check_structure.php # wrappers/check_tabs.php # wrappers/check_version_consistency.php # wrappers/check_xml_wellformed.php # wrappers/deploy_sftp.php # wrappers/fix_line_endings.php # wrappers/fix_permissions.php # wrappers/fix_tabs.php # wrappers/fix_trailing_spaces.php # wrappers/gen_wrappers.php # wrappers/index.md # wrappers/pin_action_shas.php # wrappers/plugin_health_check.php # wrappers/plugin_list.php # wrappers/plugin_metrics.php # wrappers/plugin_readiness.php # wrappers/plugin_validate.php # wrappers/scan_drift.php # wrappers/setup_labels.php # wrappers/sync_dolibarr_readmes.php # wrappers/update_sha_hashes.php # wrappers/update_version_from_readme.php
973 lines
34 KiB
PHP
Executable File
973 lines
34 KiB
PHP
Executable File
#!/usr/bin/env php
|
|
<?php
|
|
|
|
/**
|
|
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
*
|
|
* This file is part of a Moko Consulting project.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* FILE INFORMATION
|
|
<<<<<<< HEAD
|
|
* DEFGROUP: MokoCLI.Scripts.Validate
|
|
* INGROUP: MokoCLI
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
|
=======
|
|
* DEFGROUP: MokoPlatform.Scripts.Validate
|
|
* INGROUP: MokoPlatform
|
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
|
|
>>>>>>> main
|
|
* PATH: /validate/auto_detect_platform.php
|
|
* BRIEF: Automatic platform detection and validation - PHP implementation
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
|
|
|
use MokoCli\{
|
|
CliFramework,
|
|
ProjectTypeDetector,
|
|
PluginFactory,
|
|
PluginRegistry,
|
|
AuditLogger,
|
|
MetricsCollector,
|
|
SourceResolver
|
|
};
|
|
|
|
/**
|
|
* Automatic Platform Detection and Validation
|
|
*
|
|
* Detects whether a repository is a Joomla/WaaS component, Dolibarr/CRM module,
|
|
* or generic repository, then validates against appropriate schema
|
|
*/
|
|
class AutoDetectPlatform extends CliFramework
|
|
{
|
|
private const DETECTION_THRESHOLD = 0.5; // 50% confidence required
|
|
|
|
private ProjectTypeDetector $typeDetector;
|
|
private PluginFactory $pluginFactory;
|
|
|
|
private array $detectionResults = [
|
|
'client' => ['score' => 0, 'indicators' => []],
|
|
'joomla' => ['score' => 0, 'indicators' => []],
|
|
'dolibarr' => ['score' => 0, 'indicators' => []],
|
|
'nodejs' => ['score' => 0, 'indicators' => []],
|
|
'python' => ['score' => 0, 'indicators' => []],
|
|
'terraform' => ['score' => 0, 'indicators' => []],
|
|
'wordpress' => ['score' => 0, 'indicators' => []],
|
|
'mobile' => ['score' => 0, 'indicators' => []],
|
|
'api' => ['score' => 0, 'indicators' => []],
|
|
'mcp-server' => ['score' => 0, 'indicators' => []],
|
|
'documentation' => ['score' => 0, 'indicators' => []],
|
|
'generic' => ['score' => 0, 'indicators' => []],
|
|
];
|
|
|
|
private string $detectedPlatform = 'generic';
|
|
private string $schemaFile = '';
|
|
private ?object $detectedPlugin = null;
|
|
|
|
protected function configure(): void
|
|
{
|
|
$this->setDescription('Automatically detect platform type and validate repository');
|
|
$this->addArgument('--repo-path', 'Path to repository to analyze', '.');
|
|
$this->addArgument('--schema-dir', 'Path to schema definitions directory', 'definitions/default');
|
|
$this->addArgument('--output-dir', 'Directory for output reports', 'var/logs/validation');
|
|
}
|
|
|
|
protected function run(): int
|
|
{
|
|
$repoPath = $this->getArgument('--repo-path', '.');
|
|
$schemaDir = $this->getArgument('--schema-dir', 'definitions/default');
|
|
$outputDir = $this->getArgument('--output-dir', 'var/logs/validation');
|
|
|
|
// Make paths absolute
|
|
$repoPath = $this->getAbsolutePath($repoPath);
|
|
$schemaDir = $this->getAbsolutePath($schemaDir);
|
|
$outputDir = $this->getAbsolutePath($outputDir);
|
|
|
|
if (!is_dir($repoPath)) {
|
|
$this->log("Repository path not found: {$repoPath}", 'ERROR');
|
|
return 3;
|
|
}
|
|
|
|
if (!is_dir($schemaDir)) {
|
|
$this->log("Schema directory not found: {$schemaDir} (schema validation skipped)", 'WARN');
|
|
$schemaDir = '';
|
|
}
|
|
|
|
$this->log("Analyzing repository: {$repoPath}", 'INFO');
|
|
|
|
// Initialize plugin system
|
|
$logger = new AuditLogger('auto_detect_platform');
|
|
$metrics = new MetricsCollector();
|
|
$this->pluginFactory = new PluginFactory($logger, $metrics);
|
|
$this->typeDetector = new ProjectTypeDetector($logger);
|
|
|
|
// Use the new plugin system for detection
|
|
$this->log("Using ProjectTypeDetector for platform detection", 'INFO');
|
|
$detectionResult = $this->typeDetector->detect($repoPath);
|
|
|
|
if (!empty($detectionResult['type'])) {
|
|
$this->detectedPlatform = $detectionResult['type'];
|
|
$this->log("Detected platform via plugin system: {$this->detectedPlatform}", 'INFO');
|
|
|
|
// Try to get the plugin for this type
|
|
$this->detectedPlugin = $this->pluginFactory->createForProject($repoPath);
|
|
|
|
if ($this->detectedPlugin) {
|
|
$this->log("Loaded plugin: {$this->detectedPlugin->getPluginName()}", 'INFO');
|
|
|
|
// Update detection results with plugin info
|
|
$this->detectionResults[$this->detectedPlatform] = [
|
|
'score' => $detectionResult['confidence'] ?? 1.0,
|
|
'indicators' => $detectionResult['indicators'] ?? [],
|
|
];
|
|
}
|
|
} else {
|
|
// Fallback to legacy detection if plugin system doesn't detect anything
|
|
$this->log("Plugin system did not detect type, using legacy detection", 'WARNING');
|
|
|
|
// Run platform detection using legacy methods
|
|
// Client must run BEFORE Joomla — client repos contain Joomla dirs
|
|
// but are NOT Joomla extensions
|
|
$this->detectClient($repoPath);
|
|
$this->detectJoomla($repoPath);
|
|
$this->detectDolibarr($repoPath);
|
|
$this->detectNodeJS($repoPath);
|
|
$this->detectPython($repoPath);
|
|
$this->detectTerraform($repoPath);
|
|
$this->detectWordPress($repoPath);
|
|
$this->detectMobile($repoPath);
|
|
$this->detectAPI($repoPath);
|
|
$this->detectMcpServer($repoPath);
|
|
|
|
// Determine platform
|
|
$this->determinePlatform();
|
|
}
|
|
|
|
// Map to schema file
|
|
$this->schemaFile = $this->mapPlatformToSchema($schemaDir);
|
|
|
|
if (!file_exists($this->schemaFile)) {
|
|
$this->log("Schema file not found: {$this->schemaFile}", 'ERROR');
|
|
return 3;
|
|
}
|
|
|
|
// Output results
|
|
if ($this->getArgument("--json", false)) {
|
|
$this->outputJson();
|
|
} else {
|
|
$this->displayResults();
|
|
}
|
|
|
|
// Generate reports
|
|
$this->generateReports($outputDir, $repoPath);
|
|
|
|
$this->log("Platform detection completed: {$this->detectedPlatform}", 'INFO');
|
|
$this->log("Schema file: {$this->schemaFile}", 'INFO');
|
|
|
|
if ($this->detectedPlugin) {
|
|
$this->log("Plugin available for validation and health checks", 'INFO');
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Detect client site repository.
|
|
* Client repos have either:
|
|
* (a) src/ with Joomla site structure + deployment configs (legacy)
|
|
* (b) src/templateDetails.xml with type="file" (theme package)
|
|
* They are NOT Joomla extensions (component/module/plugin/template).
|
|
*/
|
|
private function detectClient(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Strong indicator: type="file" manifest (client theme package)
|
|
$manifests = glob($repoPath . '/src/*.xml') ?: [];
|
|
$isFilePackage = false;
|
|
foreach ($manifests as $xml) {
|
|
$content = @file_get_contents($xml);
|
|
if ($content && preg_match('/<extension\s+[^>]*type="file"/', $content)) {
|
|
$score += 60;
|
|
$indicators[] = 'Found Joomla type="file" manifest (theme package)';
|
|
$isFilePackage = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Theme package files
|
|
$themeMarkers = [
|
|
'src/media/templates/site/mokoonyx/css/theme/light.custom.css' => 15,
|
|
'src/media/templates/site/mokoonyx/css/theme/dark.custom.css' => 15,
|
|
'src/script.php' => 10,
|
|
'updates.xml' => 10,
|
|
];
|
|
foreach ($themeMarkers as $path => $weight) {
|
|
$full = $repoPath . '/' . $path;
|
|
if (is_file($full)) {
|
|
$score += $weight;
|
|
$indicators[] = "Found: {$path} (+{$weight})";
|
|
}
|
|
}
|
|
|
|
// Legacy indicators: deployment/monitoring configs
|
|
$clientMarkers = [
|
|
'scripts/sftp-config' => 30,
|
|
'scripts/sftp-config/sftp-config.dev.json' => 10,
|
|
'scripts/sftp-config/sftp-config.rs.json' => 10,
|
|
'monitoring/grafana' => 20,
|
|
'scripts/sync-dev-to-live.sh' => 15,
|
|
'scripts/joomla-monitor.sh' => 10,
|
|
'scripts/joomla-monitor.sites.conf' => 10,
|
|
];
|
|
|
|
foreach ($clientMarkers as $path => $weight) {
|
|
$full = $repoPath . '/' . $path;
|
|
if (is_dir($full) || is_file($full)) {
|
|
$score += $weight;
|
|
$indicators[] = "Found: {$path} (+{$weight})";
|
|
}
|
|
}
|
|
|
|
// Legacy: site structure inside source/ or src/
|
|
$srcName = SourceResolver::resolve($repoPath);
|
|
$siteDirs = ["{$srcName}/administrator", "{$srcName}/components", "{$srcName}/plugins", "{$srcName}/templates", "{$srcName}/media"];
|
|
$siteDirCount = 0;
|
|
foreach ($siteDirs as $dir) {
|
|
if (is_dir($repoPath . '/' . $dir)) {
|
|
$siteDirCount++;
|
|
}
|
|
}
|
|
if ($siteDirCount >= 3) {
|
|
$score += 20;
|
|
$indicators[] = "Joomla site structure in {$srcName}/ ({$siteDirCount}/5 dirs)";
|
|
}
|
|
|
|
// Negative: if there's a Joomla extension manifest (not type="file"), it's an extension
|
|
if (!$isFilePackage) {
|
|
foreach ($manifests as $xml) {
|
|
$content = @file_get_contents($xml);
|
|
if ($content && preg_match('/<extension\s+[^>]*type="(component|module|plugin|template|package)"/', $content)) {
|
|
$score -= 50;
|
|
$indicators[] = "Has Joomla extension manifest — likely extension, not client";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->detectionResults['client'] = [
|
|
'score' => max(0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectJoomla(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Look for Joomla manifest files
|
|
$manifests = $this->findFiles($repoPath, '*.xml', 3);
|
|
foreach ($manifests as $manifest) {
|
|
$content = @file_get_contents($manifest);
|
|
if (
|
|
$content && (
|
|
strpos($content, '<extension') !== false ||
|
|
strpos($content, '<install') !== false
|
|
)
|
|
) {
|
|
$score += 0.3;
|
|
$indicators[] = "Found Joomla manifest: " . basename($manifest);
|
|
}
|
|
}
|
|
|
|
// Check for Joomla directory structure
|
|
$joomlaDirs = ['site', 'admin', 'administrator', 'language', 'media'];
|
|
foreach ($joomlaDirs as $dir) {
|
|
if (is_dir("{$repoPath}/{$dir}")) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found Joomla directory: {$dir}/";
|
|
}
|
|
}
|
|
|
|
// Check for index.html files (Joomla security pattern)
|
|
$indexCount = count($this->findFiles($repoPath, 'index.html', 2));
|
|
if ($indexCount > 2) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found {$indexCount} index.html files (Joomla pattern)";
|
|
}
|
|
|
|
$this->detectionResults['joomla'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectDolibarr(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Look for Dolibarr module descriptor
|
|
$descriptors = $this->findFiles($repoPath, 'mod*.class.php', 3);
|
|
foreach ($descriptors as $descriptor) {
|
|
$content = @file_get_contents($descriptor);
|
|
if ($content && strpos($content, 'DolibarrModules') !== false) {
|
|
$score += 0.4;
|
|
$indicators[] = "Found Dolibarr module descriptor: " . basename($descriptor);
|
|
}
|
|
}
|
|
|
|
// Check for Dolibarr-specific code patterns
|
|
$phpFiles = $this->findFiles($repoPath, '*.php', 3);
|
|
$dolibarrPatterns = ['dol_include_once', '$this->numero', 'DoliDB', 'Translate'];
|
|
|
|
foreach ($phpFiles as $file) {
|
|
$content = @file_get_contents($file);
|
|
if (!$content) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($dolibarrPatterns as $pattern) {
|
|
if (strpos($content, $pattern) !== false) {
|
|
$score += 0.05;
|
|
$indicators[] = "Found Dolibarr pattern '{$pattern}' in " . basename($file);
|
|
break; // Only count once per file
|
|
}
|
|
}
|
|
|
|
if ($score >= 0.8) {
|
|
break; // Stop early if confident
|
|
}
|
|
}
|
|
|
|
// Check for Dolibarr directory structure
|
|
$dolibarrDirs = ['core/modules', 'sql', 'class', 'lib', 'langs'];
|
|
foreach ($dolibarrDirs as $dir) {
|
|
if (is_dir("{$repoPath}/{$dir}")) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found Dolibarr directory: {$dir}/";
|
|
}
|
|
}
|
|
|
|
// Check for SQL files in sql/ directory
|
|
if (is_dir("{$repoPath}/sql")) {
|
|
$sqlFiles = $this->findFiles("{$repoPath}/sql", '*.sql', 1);
|
|
if (count($sqlFiles) > 0) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found " . count($sqlFiles) . " SQL files in sql/";
|
|
}
|
|
}
|
|
|
|
$this->detectionResults['dolibarr'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectNodeJS(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Check for package.json
|
|
if (file_exists("{$repoPath}/package.json")) {
|
|
$score += 0.5;
|
|
$indicators[] = "Found package.json";
|
|
|
|
$content = @file_get_contents("{$repoPath}/package.json");
|
|
if ($content) {
|
|
if (strpos($content, '"typescript"') !== false || strpos($content, '"@types/') !== false) {
|
|
$score += 0.1;
|
|
$indicators[] = "TypeScript dependencies detected";
|
|
}
|
|
if (
|
|
strpos($content, '"react"') !== false || strpos($content, '"vue"') !== false ||
|
|
strpos($content, '"angular"') !== false || strpos($content, '"express"') !== false
|
|
) {
|
|
$score += 0.1;
|
|
$indicators[] = "Node.js framework detected";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for node_modules and lock files
|
|
if (is_dir("{$repoPath}/node_modules")) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found node_modules directory";
|
|
}
|
|
|
|
if (
|
|
file_exists("{$repoPath}/package-lock.json") || file_exists("{$repoPath}/yarn.lock") ||
|
|
file_exists("{$repoPath}/pnpm-lock.yaml") || file_exists("{$repoPath}/bun.lockb")
|
|
) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found package lock file";
|
|
}
|
|
|
|
// Check for TypeScript config
|
|
if (file_exists("{$repoPath}/tsconfig.json")) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found tsconfig.json";
|
|
}
|
|
|
|
$this->detectionResults['nodejs'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectPython(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Check for Python package files
|
|
if (file_exists("{$repoPath}/setup.py") || file_exists("{$repoPath}/pyproject.toml")) {
|
|
$score += 0.5;
|
|
$indicators[] = "Found Python package configuration";
|
|
}
|
|
|
|
if (file_exists("{$repoPath}/requirements.txt")) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found requirements.txt";
|
|
}
|
|
|
|
if (file_exists("{$repoPath}/Pipfile") || file_exists("{$repoPath}/poetry.lock")) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found Python dependency manager config";
|
|
}
|
|
|
|
// Check for Python files
|
|
$pyFiles = $this->findFiles($repoPath, '*.py', 2);
|
|
if (count($pyFiles) > 0) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found " . count($pyFiles) . " Python files";
|
|
}
|
|
|
|
// Check for virtual environment directories
|
|
$venvDirs = ['venv', '.venv', 'env', '.env'];
|
|
foreach ($venvDirs as $dir) {
|
|
if (is_dir("{$repoPath}/{$dir}")) {
|
|
$score += 0.05;
|
|
$indicators[] = "Found virtual environment: {$dir}/";
|
|
break;
|
|
}
|
|
}
|
|
|
|
$this->detectionResults['python'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectTerraform(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Check for Terraform files
|
|
$tfFiles = $this->findFiles($repoPath, '*.tf', 3);
|
|
if (count($tfFiles) > 0) {
|
|
$score += 0.5;
|
|
$indicators[] = "Found " . count($tfFiles) . " Terraform files";
|
|
}
|
|
|
|
// Check for terraform.tfvars or *.tfvars
|
|
$tfvarsFiles = $this->findFiles($repoPath, '*.tfvars', 2);
|
|
if (count($tfvarsFiles) > 0) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found Terraform variables files";
|
|
}
|
|
|
|
// Check for .terraform directory
|
|
if (is_dir("{$repoPath}/.terraform")) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found .terraform directory";
|
|
}
|
|
|
|
// Check for terraform.lock.hcl
|
|
if (file_exists("{$repoPath}/.terraform.lock.hcl")) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found Terraform lock file";
|
|
}
|
|
|
|
// Check for main.tf, variables.tf, outputs.tf (common pattern)
|
|
$commonFiles = ['main.tf', 'variables.tf', 'outputs.tf'];
|
|
$foundCommon = 0;
|
|
foreach ($commonFiles as $file) {
|
|
if (file_exists("{$repoPath}/{$file}")) {
|
|
$foundCommon++;
|
|
}
|
|
}
|
|
if ($foundCommon >= 2) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found standard Terraform structure";
|
|
}
|
|
|
|
$this->detectionResults['terraform'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectWordPress(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Check for plugin header
|
|
$phpFiles = $this->findFiles($repoPath, '*.php', 2);
|
|
foreach ($phpFiles as $file) {
|
|
$content = @file_get_contents($file);
|
|
if (
|
|
$content && (strpos($content, 'Plugin Name:') !== false ||
|
|
strpos($content, 'Theme Name:') !== false)
|
|
) {
|
|
$score += 0.5;
|
|
$indicators[] = "Found WordPress plugin/theme header in " . basename($file);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check for WordPress functions
|
|
$wpFunctions = ['add_action', 'add_filter', 'wp_enqueue_script', 'register_activation_hook'];
|
|
foreach ($phpFiles as $file) {
|
|
$content = @file_get_contents($file);
|
|
if (!$content) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($wpFunctions as $func) {
|
|
if (strpos($content, $func) !== false) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found WordPress function '{$func}'";
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for WordPress directory structure
|
|
$wpDirs = ['includes', 'templates', 'assets'];
|
|
foreach ($wpDirs as $dir) {
|
|
if (is_dir("{$repoPath}/{$dir}")) {
|
|
$score += 0.05;
|
|
$indicators[] = "Found WordPress directory: {$dir}/";
|
|
}
|
|
}
|
|
|
|
$this->detectionResults['wordpress'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectMobile(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Check for React Native
|
|
if (file_exists("{$repoPath}/package.json")) {
|
|
$content = @file_get_contents("{$repoPath}/package.json");
|
|
if ($content && strpos($content, '"react-native"') !== false) {
|
|
$score += 0.5;
|
|
$indicators[] = "Found React Native in package.json";
|
|
}
|
|
}
|
|
|
|
// Check for Flutter
|
|
if (file_exists("{$repoPath}/pubspec.yaml")) {
|
|
$content = @file_get_contents("{$repoPath}/pubspec.yaml");
|
|
if ($content && strpos($content, 'flutter:') !== false) {
|
|
$score += 0.5;
|
|
$indicators[] = "Found Flutter in pubspec.yaml";
|
|
}
|
|
}
|
|
|
|
// Check for iOS project
|
|
$xcodeFiles = $this->findFiles($repoPath, '*.xcodeproj', 2);
|
|
if (count($xcodeFiles) > 0) {
|
|
$score += 0.3;
|
|
$indicators[] = "Found Xcode project";
|
|
}
|
|
|
|
// Check for Android project
|
|
if (file_exists("{$repoPath}/build.gradle") || file_exists("{$repoPath}/app/build.gradle")) {
|
|
$content = @file_get_contents("{$repoPath}/build.gradle") ?: @file_get_contents("{$repoPath}/app/build.gradle");
|
|
if ($content && strpos($content, 'com.android.application') !== false) {
|
|
$score += 0.3;
|
|
$indicators[] = "Found Android application gradle";
|
|
}
|
|
}
|
|
|
|
// Check for mobile directories
|
|
$mobileDirs = ['ios', 'android', 'lib'];
|
|
$foundCount = 0;
|
|
foreach ($mobileDirs as $dir) {
|
|
if (is_dir("{$repoPath}/{$dir}")) {
|
|
$foundCount++;
|
|
}
|
|
}
|
|
if ($foundCount >= 2) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found mobile platform directories";
|
|
}
|
|
|
|
$this->detectionResults['mobile'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectAPI(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Check for API documentation files
|
|
$apiDocs = ['openapi.yaml', 'openapi.json', 'swagger.yaml', 'swagger.json', 'api.yaml'];
|
|
foreach ($apiDocs as $doc) {
|
|
if (file_exists("{$repoPath}/{$doc}")) {
|
|
$score += 0.3;
|
|
$indicators[] = "Found API documentation: {$doc}";
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check for GraphQL schema
|
|
$graphqlFiles = $this->findFiles($repoPath, '*.graphql', 2);
|
|
if (count($graphqlFiles) > 0 || file_exists("{$repoPath}/schema.graphql")) {
|
|
$score += 0.3;
|
|
$indicators[] = "Found GraphQL schema";
|
|
}
|
|
|
|
// Check for gRPC proto files
|
|
$protoFiles = $this->findFiles($repoPath, '*.proto', 2);
|
|
if (count($protoFiles) > 0) {
|
|
$score += 0.3;
|
|
$indicators[] = "Found Protocol Buffer definitions";
|
|
}
|
|
|
|
// Check for Dockerfile (common in microservices)
|
|
if (file_exists("{$repoPath}/Dockerfile")) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found Dockerfile";
|
|
}
|
|
|
|
// Check for docker-compose.yml
|
|
if (file_exists("{$repoPath}/docker-compose.yml") || file_exists("{$repoPath}/docker-compose.yaml")) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found docker-compose configuration";
|
|
}
|
|
|
|
// Check for API patterns in code
|
|
$apiFiles = array_merge(
|
|
$this->findFiles($repoPath, '*.js', 2),
|
|
$this->findFiles($repoPath, '*.ts', 2),
|
|
$this->findFiles($repoPath, '*.py', 2)
|
|
);
|
|
|
|
$apiPatterns = [
|
|
'@app.route' => 'Flask route',
|
|
'@api_view' => 'Django REST framework',
|
|
'express()' => 'Express.js',
|
|
'fastapi' => 'FastAPI',
|
|
'@Controller' => 'NestJS controller',
|
|
];
|
|
|
|
foreach ($apiFiles as $file) {
|
|
$content = @file_get_contents($file);
|
|
if (!$content) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($apiPatterns as $pattern => $name) {
|
|
if (stripos($content, $pattern) !== false) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found {$name} pattern";
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->detectionResults['api'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function detectMcpServer(string $repoPath): void
|
|
{
|
|
$score = 0;
|
|
$indicators = [];
|
|
|
|
// Check for MCP SDK in package.json
|
|
if (file_exists("{$repoPath}/package.json")) {
|
|
$content = @file_get_contents("{$repoPath}/package.json");
|
|
if ($content && strpos($content, '@modelcontextprotocol/sdk') !== false) {
|
|
$score += 0.5;
|
|
$indicators[] = "Found @modelcontextprotocol/sdk in package.json";
|
|
}
|
|
}
|
|
|
|
// Check for MCP server entry point with McpServer usage
|
|
$mcpEntry = SourceResolver::findUnderSource($repoPath, 'index.ts');
|
|
if ($mcpEntry !== null) {
|
|
$content = @file_get_contents($mcpEntry);
|
|
$mcpSrcName = SourceResolver::resolve($repoPath);
|
|
if ($content) {
|
|
if (strpos($content, 'McpServer') !== false) {
|
|
$score += 0.3;
|
|
$indicators[] = "Found McpServer import in {$mcpSrcName}/index.ts";
|
|
}
|
|
if (strpos($content, 'server.tool(') !== false) {
|
|
$score += 0.1;
|
|
$toolCount = substr_count($content, 'server.tool(');
|
|
$indicators[] = "Found {$toolCount} tool registrations in {$mcpSrcName}/index.ts";
|
|
}
|
|
if (strpos($content, 'StdioServerTransport') !== false) {
|
|
$score += 0.1;
|
|
$indicators[] = "Found StdioServerTransport (stdio MCP transport)";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for the standard 4-file MCP structure
|
|
$mcpRequired = ['index.ts', 'client.ts', 'config.ts', 'types.ts'];
|
|
$foundCount = 0;
|
|
foreach ($mcpRequired as $file) {
|
|
if (SourceResolver::findUnderSource($repoPath, $file) !== null) {
|
|
$foundCount++;
|
|
}
|
|
}
|
|
if ($foundCount === 4) {
|
|
$score += 0.1;
|
|
$mcpSrcName = $mcpSrcName ?? SourceResolver::resolve($repoPath);
|
|
$indicators[] = "Found standard MCP 4-file {$mcpSrcName}/ structure";
|
|
}
|
|
|
|
// Check for setup wizard
|
|
if (file_exists("{$repoPath}/scripts/setup.mjs")) {
|
|
$score += 0.05;
|
|
$indicators[] = "Found interactive setup wizard";
|
|
}
|
|
|
|
<<<<<<< HEAD
|
|
// Check for .MokoCLI platform declaration
|
|
$mokoFiles = ["{$repoPath}/.gitea/.MokoCLI", "{$repoPath}/.github/.MokoCLI"];
|
|
=======
|
|
// Check for .mokoplatform platform declaration
|
|
$mokoFiles = ["{$repoPath}/.gitea/.mokoplatform", "{$repoPath}/.github/.mokoplatform"];
|
|
>>>>>>> main
|
|
foreach ($mokoFiles as $mokoFile) {
|
|
if (file_exists($mokoFile)) {
|
|
$content = @file_get_contents($mokoFile);
|
|
if ($content && stripos($content, 'mcp-server') !== false) {
|
|
$score += 0.2;
|
|
$indicators[] = "Found explicit mcp-server platform declaration";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->detectionResults['mcp-server'] = [
|
|
'score' => min(1.0, $score),
|
|
'indicators' => $indicators,
|
|
];
|
|
}
|
|
|
|
private function determinePlatform(): void
|
|
{
|
|
// Find platform with highest score above threshold
|
|
$maxScore = 0;
|
|
$selectedPlatform = 'generic';
|
|
|
|
foreach ($this->detectionResults as $platform => $data) {
|
|
if ($data['score'] >= self::DETECTION_THRESHOLD && $data['score'] > $maxScore) {
|
|
$maxScore = $data['score'];
|
|
$selectedPlatform = $platform;
|
|
}
|
|
}
|
|
|
|
$this->detectedPlatform = $selectedPlatform;
|
|
}
|
|
|
|
private function mapPlatformToSchema(string $schemaDir): string
|
|
{
|
|
$mapping = [
|
|
'joomla' => 'waas-component.tf',
|
|
'dolibarr' => 'crm-module.tf',
|
|
'nodejs' => 'nodejs-repository.tf',
|
|
'python' => 'python-repository.tf',
|
|
'terraform' => 'terraform-repository.tf',
|
|
'wordpress' => 'wordpress-repository.tf',
|
|
'mobile' => 'mobile-app-repository.tf',
|
|
'api' => 'api-repository.tf',
|
|
'mcp-server' => 'mcp-server.tf',
|
|
'documentation' => 'documentation-repository.tf',
|
|
'standards' => 'standards-repository.tf',
|
|
'generic' => 'default-repository.tf',
|
|
];
|
|
|
|
return $schemaDir . '/' . $mapping[$this->detectedPlatform];
|
|
}
|
|
|
|
private function displayResults(): void
|
|
{
|
|
echo "\n=== Platform Detection Results ===\n\n";
|
|
|
|
echo "Platform: {$this->detectedPlatform}\n";
|
|
echo "Schema: {$this->schemaFile}\n\n";
|
|
|
|
echo "Detection Scores:\n";
|
|
foreach ($this->detectionResults as $platform => $data) {
|
|
$percentage = round($data['score'] * 100, 1);
|
|
$status = ($data['score'] >= self::DETECTION_THRESHOLD) ? '✅' : '❌';
|
|
echo sprintf(" %s %s: %.1f%%\n", $status, ucfirst($platform), $percentage);
|
|
}
|
|
|
|
echo "\nDetection Indicators:\n";
|
|
$indicators = $this->detectionResults[$this->detectedPlatform]['indicators'];
|
|
if (empty($indicators)) {
|
|
echo " No specific indicators found (generic repository)\n";
|
|
} else {
|
|
foreach ($indicators as $indicator) {
|
|
echo " • {$indicator}\n";
|
|
}
|
|
}
|
|
|
|
echo "\n";
|
|
}
|
|
|
|
private function outputJson(): void
|
|
{
|
|
$output = [
|
|
'platform' => $this->detectedPlatform,
|
|
'schema' => $this->schemaFile,
|
|
'detection_results' => $this->detectionResults,
|
|
'threshold' => self::DETECTION_THRESHOLD,
|
|
'timestamp' => date('c'),
|
|
'plugin_available' => $this->detectedPlugin !== null,
|
|
];
|
|
|
|
if ($this->detectedPlugin) {
|
|
$output['plugin_info'] = [
|
|
'name' => $this->detectedPlugin->getPluginName(),
|
|
'version' => $this->detectedPlugin->getPluginVersion(),
|
|
'type' => $this->detectedPlugin->getProjectType(),
|
|
];
|
|
}
|
|
|
|
echo json_encode($output, JSON_PRETTY_PRINT) . PHP_EOL;
|
|
}
|
|
|
|
private function generateReports(string $outputDir, string $repoPath): void
|
|
{
|
|
// Ensure output directory exists
|
|
if (!is_dir($outputDir)) {
|
|
@mkdir($outputDir, 0755, true);
|
|
}
|
|
|
|
$timestamp = date('Ymd_His');
|
|
|
|
// Generate detection report
|
|
$detectionReport = $outputDir . "/detection_report_{$timestamp}.md";
|
|
$this->writeDetectionReport($detectionReport, $repoPath);
|
|
|
|
// Generate summary report
|
|
$summaryReport = $outputDir . "/SUMMARY_{$timestamp}.md";
|
|
$this->writeSummaryReport($summaryReport, $repoPath);
|
|
|
|
$this->log("Reports generated in: {$outputDir}", 'INFO');
|
|
}
|
|
|
|
private function writeDetectionReport(string $file, string $repoPath): void
|
|
{
|
|
$content = "# Platform Detection Report\n\n";
|
|
$content .= "**Generated**: " . date('Y-m-d H:i:s') . "\n";
|
|
$content .= "**Repository**: {$repoPath}\n\n";
|
|
|
|
$content .= "## Detected Platform\n\n";
|
|
$content .= "**Type**: " . strtoupper($this->detectedPlatform) . "\n";
|
|
$content .= "**Confidence**: " . round($this->detectionResults[$this->detectedPlatform]['score'] * 100, 1) . "%\n";
|
|
$content .= "**Schema**: {$this->schemaFile}\n\n";
|
|
|
|
$content .= "## Detection Indicators\n\n";
|
|
foreach ($this->detectionResults[$this->detectedPlatform]['indicators'] as $indicator) {
|
|
$content .= "- {$indicator}\n";
|
|
}
|
|
|
|
$content .= "\n## All Platform Scores\n\n";
|
|
foreach ($this->detectionResults as $platform => $data) {
|
|
$percentage = round($data['score'] * 100, 1);
|
|
$content .= "- **" . ucfirst($platform) . "**: {$percentage}%\n";
|
|
}
|
|
|
|
@file_put_contents($file, $content);
|
|
}
|
|
|
|
private function writeSummaryReport(string $file, string $repoPath): void
|
|
{
|
|
$content = "# Platform Detection Summary\n\n";
|
|
$content .= "| Property | Value |\n";
|
|
$content .= "|----------|-------|\n";
|
|
$content .= "| Repository | {$repoPath} |\n";
|
|
$content .= "| Platform | " . strtoupper($this->detectedPlatform) . " |\n";
|
|
$content .= "| Confidence | " . round($this->detectionResults[$this->detectedPlatform]['score'] * 100, 1) . "% |\n";
|
|
$content .= "| Schema | " . basename($this->schemaFile) . " |\n";
|
|
$content .= "| Timestamp | " . date('Y-m-d H:i:s') . " |\n\n";
|
|
|
|
$content .= "## Next Steps\n\n";
|
|
$content .= "1. Review detection indicators\n";
|
|
$content .= "2. Validate repository against schema: {$this->schemaFile}\n";
|
|
$content .= "3. Address any validation errors or warnings\n";
|
|
|
|
@file_put_contents($file, $content);
|
|
}
|
|
|
|
private function findFiles(string $dir, string $pattern, int $maxDepth = 1): array
|
|
{
|
|
$files = [];
|
|
$pattern = str_replace('*', '.*', $pattern);
|
|
$pattern = str_replace('.', '\.', $pattern);
|
|
|
|
try {
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
|
|
RecursiveIteratorIterator::SELF_FIRST
|
|
);
|
|
$iterator->setMaxDepth($maxDepth);
|
|
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile() && preg_match("/{$pattern}$/", $file->getFilename())) {
|
|
$files[] = $file->getPathname();
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
// Directory not accessible
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
private function getAbsolutePath(string $path): string
|
|
{
|
|
if (strlen($path) > 0 && $path[0] === '/') {
|
|
return $path;
|
|
}
|
|
|
|
return getcwd() . '/' . $path;
|
|
}
|
|
}
|
|
|
|
// Run the application
|
|
$app = new AutoDetectPlatform();
|
|
exit($app->execute());
|