WizdomWeb/app/Utils/ClassInspector.php

170 lines
5.5 KiB
PHP

<?php
namespace WizdomNetworks\WizeWeb\Utils;
/**
* Utility class to inspect declared classes and autoloading in the application.
*/
class ClassInspector
{
/**
* Get all declared classes within a given namespace.
*
* @param string $namespace The namespace to filter by.
* @return array An array of fully qualified class names in the namespace.
*/
public static function getClassesInNamespace(string $namespace): array
{
$classes = get_declared_classes();
$namespaceClasses = [];
foreach ($classes as $class) {
if (str_starts_with($class, $namespace . '\\')) {
$namespaceClasses[] = $class;
}
}
return $namespaceClasses;
}
/**
* Detect the top-level namespace of the current application.
* Assumes PSR-4 autoloading and looks at composer.json.
*
* @return string|null The detected namespace, or null if not found.
*/
public static function getTopLevelNamespace(): ?string
{
$composerFile = __DIR__ . '/../../composer.json'; // Adjust path as needed
if (!file_exists($composerFile)) {
return null;
}
$composerData = json_decode(file_get_contents($composerFile), true);
if (!isset($composerData['autoload']['psr-4'])) {
return null;
}
$namespaces = array_keys($composerData['autoload']['psr-4']);
return rtrim($namespaces[0], '\\'); // Return the first namespace as the top-level
}
/**
* Compare declared classes with all possible classes in the namespace directory.
*
* @param string $namespace The namespace to inspect.
* @param string $baseDir The base directory for the namespace.
* @return array An array with loaded and not-loaded class information.
*/
public static function compareLoadedClasses(string $namespace, string $baseDir): array
{
$loadedClasses = self::getClassesInNamespace($namespace);
// Get all possible classes from the directory
$allClasses = [];
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($baseDir));
foreach ($iterator as $file) {
if ($file->getExtension() === 'php') {
$relativePath = str_replace([$baseDir, '/', '.php'], ['', '\\', ''], $file->getPathname());
$className = $namespace . '\\' . $relativePath;
$className = str_replace('\\\\', '\\', $className); // Remove double backslashes
$allClasses[$className] = $file->getPathname();
}
}
// Separate loaded and not loaded
$classDetails = [];
foreach ($allClasses as $className => $path) {
$status = class_exists($className, true) ? 'Loaded' : 'Not Loaded';
$classDetails[] = [
'class' => self::getClassNameOnly($className),
'path' => $path,
'status' => $status,
];
}
return $classDetails;
}
/**
* Extract the class name from a fully qualified class name.
*
* @param string $class Fully qualified class name.
* @return string The class name without the namespace.
*/
private static function getClassNameOnly(string $class): string
{
$parts = explode('\\', $class);
return end($parts);
}
/**
* Display the results in a table format.
*
* @param string $namespace The namespace to inspect.
* @param string $baseDir The base directory for the namespace.
* @param bool $useHtml Whether to generate the table as HTML (default: true).
* @return void
*/
public static function displayClassTable(string $namespace, string $baseDir, bool $useHtml = true): void
{
$classDetails = self::compareLoadedClasses($namespace, $baseDir);
if ($useHtml) {
// Generate HTML table
echo "<table border='1' cellspacing='0' cellpadding='5'>";
echo "<thead>";
echo "<tr><th>Class</th><th>Path</th><th>Status</th></tr>";
echo "</thead>";
echo "<tbody>";
foreach ($classDetails as $detail) {
echo "<tr>";
echo "<td>{$detail['class']}</td>";
echo "<td>{$detail['path']}</td>";
echo "<td>{$detail['status']}</td>";
echo "</tr>";
}
echo "</tbody>";
echo "</table>";
} else {
// Generate plain text table
echo str_pad("Class", 30) . str_pad("Path", 50) . "Status\n";
echo str_repeat("-", 100) . "\n";
foreach ($classDetails as $detail) {
echo str_pad($detail['class'], 30) .
str_pad($detail['path'], 50) .
$detail['status'] . "\n";
}
}
}
/**
* Debug all classes in the top-level namespace, showing a table of results.
*
* @param bool $useHtml Whether to generate the table as HTML (default: true).
* @return void
*/
public static function debugTopLevelNamespace(bool $useHtml = true): void
{
$namespace = self::getTopLevelNamespace();
if (!$namespace) {
echo "Top-level namespace could not be determined.\n";
return;
}
// Assume PSR-4 base directory is app/ (adjust if needed)
$baseDir = realpath(__DIR__ . '/../../app');
if (!$baseDir) {
echo "Base directory could not be resolved.\n";
return;
}
self::displayClassTable($namespace, $baseDir, $useHtml);
}
}