chore: cascade main → dev (44c6bcb) [skip ci]
#112
@@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/client_health_check.php
|
||||
* BRIEF: Verify a client site's update server, installed version, and release availability
|
||||
*
|
||||
* Usage:
|
||||
* php client_health_check.php --update-url URL
|
||||
* php client_health_check.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (reads update server URL from manifest)
|
||||
* --update-url Update server XML URL (overrides manifest)
|
||||
* --site-url Live site URL for version checking via Joomla API (optional)
|
||||
* --api-token Joomla API token for site-url (optional)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$updateUrl = null;
|
||||
$siteUrl = null;
|
||||
$apiToken = null;
|
||||
$ghOutput = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--update-url' && isset($argv[$i + 1])) $updateUrl = $argv[$i + 1];
|
||||
if ($arg === '--site-url' && isset($argv[$i + 1])) $siteUrl = $argv[$i + 1];
|
||||
if ($arg === '--api-token' && isset($argv[$i + 1])) $apiToken = $argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
$checks = [];
|
||||
|
||||
// ── Resolve update server URL from manifest ─────────────────────────────
|
||||
if ($updateUrl === null) {
|
||||
$searchDirs = ["{$root}/src", $root];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
$xml = file_get_contents($f);
|
||||
if (preg_match('/<server[^>]*>([^<]+)<\/server>/', $xml, $m)) {
|
||||
$updateUrl = trim($m[1]);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($updateUrl === null) {
|
||||
fwrite(STDERR, "No update server URL found. Use --update-url or provide a manifest with <updateservers>.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Update server: {$updateUrl}\n\n";
|
||||
|
||||
// ── Check 1: Update server accessible ───────────────────────────────────
|
||||
echo "--- Update Server ---\n";
|
||||
$ch = curl_init($updateUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTPHEADER => ['User-Agent: MokoHealthCheck/1.0'],
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200 && !empty($response)) {
|
||||
echo " PASS: HTTP {$httpCode}, " . strlen($response) . " bytes\n";
|
||||
$checks['update_server'] = 'pass';
|
||||
} else {
|
||||
echo " FAIL: HTTP {$httpCode}\n";
|
||||
$checks['update_server'] = 'fail';
|
||||
}
|
||||
|
||||
// ── Check 2: Parse updates.xml for stable version ───────────────────────
|
||||
$stableVersion = null;
|
||||
$downloadUrl = null;
|
||||
|
||||
if (!empty($response)) {
|
||||
$sections = preg_split('/<update>/', $response);
|
||||
foreach ($sections as $section) {
|
||||
if (strpos($section, '<tag>stable</tag>') !== false) {
|
||||
if (preg_match('/<version>([^<]+)<\/version>/', $section, $m)) {
|
||||
$stableVersion = $m[1];
|
||||
}
|
||||
if (preg_match('/<downloadurl[^>]*>([^<]+)<\/downloadurl>/', $section, $m)) {
|
||||
$downloadUrl = trim($m[1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($stableVersion === null && preg_match('/<version>([^<]+)<\/version>/', $response, $m)) {
|
||||
$stableVersion = $m[1];
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n--- Stable Release ---\n";
|
||||
if ($stableVersion !== null) {
|
||||
echo " Version: {$stableVersion}\n";
|
||||
$checks['stable_version'] = $stableVersion;
|
||||
} else {
|
||||
echo " FAIL: Could not parse stable version\n";
|
||||
$checks['stable_version'] = 'fail';
|
||||
}
|
||||
|
||||
// ── Check 3: Download URL accessible ────────────────────────────────────
|
||||
if ($downloadUrl !== null) {
|
||||
echo "\n--- Download URL ---\n";
|
||||
$ch = curl_init($downloadUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_NOBODY => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
]);
|
||||
curl_exec($ch);
|
||||
$dlCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$dlSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
|
||||
curl_close($ch);
|
||||
|
||||
if ($dlCode === 200) {
|
||||
$sizeKb = $dlSize > 0 ? round($dlSize / 1024) . 'KB' : 'unknown size';
|
||||
echo " PASS: HTTP {$dlCode}, {$sizeKb}\n";
|
||||
$checks['download'] = 'pass';
|
||||
} else {
|
||||
echo " FAIL: HTTP {$dlCode}\n";
|
||||
$checks['download'] = 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Check 4: Site version (optional) ────────────────────────────────────
|
||||
if ($siteUrl !== null && $apiToken !== null) {
|
||||
echo "\n--- Site Version ---\n";
|
||||
$apiUrl = rtrim($siteUrl, '/') . '/api/index.php/v1/extensions?filter[type]=file';
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Joomla-Token: {$apiToken}",
|
||||
'Accept: application/json',
|
||||
],
|
||||
]);
|
||||
$siteResponse = curl_exec($ch);
|
||||
$siteCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($siteCode === 200) {
|
||||
echo " API accessible (HTTP {$siteCode})\n";
|
||||
$checks['site_api'] = 'pass';
|
||||
} else {
|
||||
echo " WARN: Site API returned HTTP {$siteCode}\n";
|
||||
$checks['site_api'] = 'warn';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────────
|
||||
echo "\n=== Health Check Summary ===\n";
|
||||
$failed = 0;
|
||||
foreach ($checks as $name => $result) {
|
||||
$icon = ($result === 'fail') ? 'FAIL' : (($result === 'warn') ? 'WARN' : 'OK');
|
||||
if ($result === 'fail') $failed++;
|
||||
echo " {$icon}: {$name} = {$result}\n";
|
||||
}
|
||||
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "health_status=" . ($failed > 0 ? 'fail' : 'pass') . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "health_version=" . ($stableVersion ?? 'unknown') . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "health_failures={$failed}\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
exit($failed > 0 ? 1 : 0);
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/joomla_compat_check.php
|
||||
* BRIEF: Check if extension targetplatform regex matches the latest Joomla version
|
||||
*
|
||||
* Usage:
|
||||
* php joomla_compat_check.php --path /repo
|
||||
* php joomla_compat_check.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (default: .)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$ghOutput = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
|
||||
// ── Find manifest and extract targetplatform ────────────────────────────
|
||||
$manifest = null;
|
||||
$searchDirs = ["{$root}/src", $root];
|
||||
foreach ($searchDirs as $dir) {
|
||||
if (!is_dir($dir)) continue;
|
||||
foreach (glob("{$dir}/*.xml") ?: [] as $f) {
|
||||
$xml = file_get_contents($f);
|
||||
if (strpos($xml, '<extension') !== false && strpos($xml, 'targetplatform') !== false) {
|
||||
$manifest = $f;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($manifest === null) {
|
||||
fwrite(STDERR, "No manifest with targetplatform found\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$xml = file_get_contents($manifest);
|
||||
$relManifest = str_replace($root . '/', '', $manifest);
|
||||
|
||||
// Extract targetplatform version regex
|
||||
$targetRegex = '';
|
||||
if (preg_match('/targetplatform[^>]*version="([^"]+)"/', $xml, $m)) {
|
||||
$targetRegex = $m[1];
|
||||
}
|
||||
|
||||
if (empty($targetRegex)) {
|
||||
echo "No targetplatform version found in {$relManifest}\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Manifest: {$relManifest}\n";
|
||||
echo "Target regex: {$targetRegex}\n";
|
||||
|
||||
// ── Fetch latest Joomla version ─────────────────────────────────────────
|
||||
$joomlaVersions = [];
|
||||
$updateUrl = 'https://update.joomla.org/core/sts/list_sts.xml';
|
||||
$updateXml = @file_get_contents($updateUrl);
|
||||
|
||||
if ($updateXml === false) {
|
||||
// Fallback: try the LTS feed
|
||||
$updateUrl = 'https://update.joomla.org/core/list.xml';
|
||||
$updateXml = @file_get_contents($updateUrl);
|
||||
}
|
||||
|
||||
if ($updateXml !== false) {
|
||||
// Parse all version entries
|
||||
preg_match_all('/<version>([^<]+)<\/version>/', $updateXml, $matches);
|
||||
$joomlaVersions = $matches[1] ?? [];
|
||||
}
|
||||
|
||||
if (empty($joomlaVersions)) {
|
||||
echo "WARNING: Could not fetch Joomla versions from update server\n";
|
||||
echo "Tested URL: {$updateUrl}\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Sort and get latest
|
||||
usort($joomlaVersions, 'version_compare');
|
||||
$latestJoomla = end($joomlaVersions);
|
||||
|
||||
echo "Latest Joomla: {$latestJoomla}\n";
|
||||
|
||||
// ── Test compatibility ──────────────────────────────────────────────────
|
||||
// The targetplatform regex uses Joomla's regex format
|
||||
// Common patterns: "5\.[0-9]+" or "((5.[0-9])|(6.[0-9]))"
|
||||
$compatible = @preg_match("/{$targetRegex}/", $latestJoomla);
|
||||
|
||||
if ($compatible === false) {
|
||||
echo "ERROR: Invalid regex in targetplatform: {$targetRegex}\n";
|
||||
$result = 'error';
|
||||
} elseif ($compatible === 1) {
|
||||
echo "PASS: Joomla {$latestJoomla} matches targetplatform regex\n";
|
||||
$result = 'pass';
|
||||
} else {
|
||||
// Check which major versions are supported
|
||||
$supported = [];
|
||||
foreach (['5.0', '5.1', '5.2', '5.3', '5.4', '6.0', '6.1', '6.2', '7.0'] as $v) {
|
||||
if (@preg_match("/{$targetRegex}/", $v)) {
|
||||
$supported[] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
echo "WARN: Joomla {$latestJoomla} does NOT match targetplatform regex\n";
|
||||
echo "Supported versions: " . implode(', ', $supported) . "\n";
|
||||
echo "Consider updating targetplatform to include Joomla {$latestJoomla}\n";
|
||||
$result = 'warn';
|
||||
}
|
||||
|
||||
// ── Export ───────────────────────────────────────────────────────────────
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "compat_result={$result}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "compat_joomla={$latestJoomla}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "compat_regex={$targetRegex}\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
exit($result === 'error' ? 1 : 0);
|
||||
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: moko-platform.CLI
|
||||
* INGROUP: moko-platform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /cli/theme_lint.php
|
||||
* BRIEF: Lint theme files — CSS syntax, image sizes, hardcoded URLs
|
||||
*
|
||||
* Usage:
|
||||
* php theme_lint.php --path /repo
|
||||
* php theme_lint.php --path /repo --max-image-kb 500
|
||||
* php theme_lint.php --path /repo --github-output
|
||||
*
|
||||
* Options:
|
||||
* --path Repository root (default: .)
|
||||
* --max-image-kb Maximum image file size in KB (default: 500)
|
||||
* --github-output Export results to $GITHUB_OUTPUT
|
||||
* --strict Exit 1 on any warning (default: only on errors)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = '.';
|
||||
$maxImageKb = 500;
|
||||
$ghOutput = false;
|
||||
$strict = false;
|
||||
|
||||
foreach ($argv as $i => $arg) {
|
||||
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
|
||||
if ($arg === '--max-image-kb' && isset($argv[$i + 1])) $maxImageKb = (int)$argv[$i + 1];
|
||||
if ($arg === '--github-output') $ghOutput = true;
|
||||
if ($arg === '--strict') $strict = true;
|
||||
}
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
$errors = 0;
|
||||
$warnings = 0;
|
||||
|
||||
// ── Find source directory ───────────────────────────────────────────────
|
||||
$srcDir = null;
|
||||
foreach (['src', 'htdocs'] as $d) {
|
||||
if (is_dir("{$root}/{$d}")) { $srcDir = "{$root}/{$d}"; break; }
|
||||
}
|
||||
if ($srcDir === null) {
|
||||
fwrite(STDERR, "No src/ or htdocs/ directory in {$root}\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "Theme Lint: {$srcDir}\n\n";
|
||||
|
||||
// ── Check 1: CSS syntax validation ──────────────────────────────────────
|
||||
echo "--- CSS Syntax ---\n";
|
||||
$cssFiles = findFiles($srcDir, '*.css');
|
||||
$cssMinFiles = findFiles($srcDir, '*.min.css');
|
||||
$cssToCheck = array_diff($cssFiles, $cssMinFiles);
|
||||
|
||||
if (empty($cssToCheck)) {
|
||||
echo " No CSS files to check\n";
|
||||
} else {
|
||||
foreach ($cssToCheck as $file) {
|
||||
$content = file_get_contents($file);
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
|
||||
// Check for unmatched braces
|
||||
$openBraces = substr_count($content, '{');
|
||||
$closeBraces = substr_count($content, '}');
|
||||
if ($openBraces !== $closeBraces) {
|
||||
echo " ERROR: {$relPath}: unmatched braces (open={$openBraces}, close={$closeBraces})\n";
|
||||
$errors++;
|
||||
}
|
||||
|
||||
// Check for empty rules
|
||||
if (preg_match_all('/\{[\s]*\}/', $content, $m)) {
|
||||
$count = count($m[0]);
|
||||
echo " WARN: {$relPath}: {$count} empty rule(s)\n";
|
||||
$warnings++;
|
||||
}
|
||||
|
||||
// Check for !important abuse (more than 10 in one file)
|
||||
$importantCount = substr_count($content, '!important');
|
||||
if ($importantCount > 10) {
|
||||
echo " WARN: {$relPath}: {$importantCount} !important declarations (consider refactoring)\n";
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors === 0) {
|
||||
echo " OK: " . count($cssToCheck) . " CSS file(s) checked\n";
|
||||
}
|
||||
}
|
||||
|
||||
// ── Check 2: Image file sizes ───────────────────────────────────────────
|
||||
echo "\n--- Image Sizes (max {$maxImageKb}KB) ---\n";
|
||||
$imageExts = ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.webp', '*.svg', '*.bmp'];
|
||||
$images = [];
|
||||
foreach ($imageExts as $ext) {
|
||||
$images = array_merge($images, findFiles($srcDir, $ext));
|
||||
}
|
||||
// Also check root images/ directory
|
||||
if (is_dir("{$root}/images")) {
|
||||
foreach ($imageExts as $ext) {
|
||||
$images = array_merge($images, findFiles("{$root}/images", $ext));
|
||||
}
|
||||
}
|
||||
|
||||
$oversized = 0;
|
||||
$totalSize = 0;
|
||||
foreach ($images as $file) {
|
||||
$size = filesize($file);
|
||||
$totalSize += $size;
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
$sizeKb = round($size / 1024);
|
||||
|
||||
if ($sizeKb > $maxImageKb) {
|
||||
echo " WARN: {$relPath}: {$sizeKb}KB (exceeds {$maxImageKb}KB limit)\n";
|
||||
$oversized++;
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
|
||||
$totalMb = round($totalSize / 1024 / 1024, 1);
|
||||
echo " " . count($images) . " image(s), {$totalMb}MB total";
|
||||
if ($oversized > 0) {
|
||||
echo ", {$oversized} oversized";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
// ── Check 3: Hardcoded URLs in CSS/JS ───────────────────────────────────
|
||||
echo "\n--- Hardcoded URLs ---\n";
|
||||
$codeFiles = array_merge(
|
||||
findFiles($srcDir, '*.css'),
|
||||
findFiles($srcDir, '*.js')
|
||||
);
|
||||
// Exclude minified files
|
||||
$codeFiles = array_filter($codeFiles, function($f) {
|
||||
return !preg_match('/\.min\.(css|js)$/', $f);
|
||||
});
|
||||
|
||||
$urlPatterns = [
|
||||
'/https?:\/\/clarksvillefurs\.com/' => 'hardcoded production URL',
|
||||
'/https?:\/\/[a-z]+\.dev\.mokoconsulting\.tech/' => 'hardcoded dev URL',
|
||||
'/https?:\/\/localhost/' => 'localhost reference',
|
||||
];
|
||||
|
||||
$urlIssues = 0;
|
||||
foreach ($codeFiles as $file) {
|
||||
$content = file_get_contents($file);
|
||||
$relPath = str_replace($root . '/', '', $file);
|
||||
|
||||
foreach ($urlPatterns as $pattern => $desc) {
|
||||
if (preg_match_all($pattern, $content, $matches)) {
|
||||
$count = count($matches[0]);
|
||||
echo " WARN: {$relPath}: {$count} {$desc}\n";
|
||||
$urlIssues++;
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($urlIssues === 0) {
|
||||
echo " OK: No hardcoded URLs found\n";
|
||||
}
|
||||
|
||||
// ── Summary ─────────────────────────────────────────────────────────────
|
||||
echo "\n=== Summary ===\n";
|
||||
echo "Errors: {$errors}\n";
|
||||
echo "Warnings: {$warnings}\n";
|
||||
|
||||
if ($ghOutput) {
|
||||
$ghFile = getenv('GITHUB_OUTPUT');
|
||||
if ($ghFile) {
|
||||
file_put_contents($ghFile, "lint_errors={$errors}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_warnings={$warnings}\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_images=" . count($images) . "\n", FILE_APPEND);
|
||||
file_put_contents($ghFile, "lint_css=" . count($cssToCheck) . "\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors > 0) {
|
||||
exit(1);
|
||||
}
|
||||
if ($strict && $warnings > 0) {
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
|
||||
// ── Helper: recursively find files matching a glob pattern ──────────────
|
||||
function findFiles(string $dir, string $pattern): array
|
||||
{
|
||||
$results = [];
|
||||
if (!is_dir($dir)) return $results;
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if (fnmatch($pattern, $file->getFilename())) {
|
||||
$results[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
Reference in New Issue
Block a user