3rd commit

This commit is contained in:
overplayed 2025-01-29 12:06:27 -05:00
parent 27261b1e50
commit 2645d45082
7 changed files with 260 additions and 126 deletions

View File

@ -2,8 +2,10 @@
namespace WizdomNetworks\WizeWeb\Utils;
use WizdomNetworks\WizeWeb\Utils\Logger;
/**
* Error Handler Utility
* ErrorHandler Utility
*
* A utility for handling errors and exceptions globally.
*
@ -49,10 +51,35 @@ class ErrorHandler
*/
public static function handleException(\Throwable $exception): void
{
$message = "Exception: " . $exception->getMessage() . " in " . $exception->getFile() . " on line " . $exception->getLine();
$message = sprintf(
"Exception: %s in %s on line %d\nStack trace:\n%s",
$exception->getMessage(),
$exception->getFile(),
$exception->getLine(),
$exception->getTraceAsString()
);
Logger::error($message);
http_response_code(500);
echo "An internal error occurred. Please try again later.";
exit;
}
/**
* Handles fatal errors and shuts down the application gracefully.
*/
public static function shutdown(): void
{
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) {
$message = sprintf(
"Fatal Error: %s in %s on line %d",
$error['message'],
$error['file'],
$error['line']
);
Logger::error($message);
http_response_code(500);
echo "A critical error occurred. Please contact support.";
}
}
}

View File

@ -2,6 +2,8 @@
namespace WizdomNetworks\WizeWeb\Utils;
use WizdomNetworks\WizeWeb\Utils\ErrorHandler;
/**
* Logger Utility
*
@ -27,7 +29,13 @@ class Logger
$timestamp = date('Y-m-d H:i:s');
$formattedMessage = "[$timestamp] [$level]: $message" . PHP_EOL;
file_put_contents(self::$logFile, $formattedMessage, FILE_APPEND | LOCK_EX);
try {
file_put_contents(self::$logFile, $formattedMessage, FILE_APPEND | LOCK_EX);
} catch (\Throwable $e) {
ErrorHandler::exception($e);
// Fallback to PHP's error_log if writing to the file fails
error_log("[ERROR] Logging to file failed: " . $e->getMessage());
}
}
/**
@ -59,4 +67,14 @@ class Logger
{
self::log('ERROR', $message);
}
/**
* Sets the log file path.
*
* @param string $filePath The path to the log file.
*/
public static function setLogFile(string $filePath): void
{
self::$logFile = $filePath;
}
}

View File

@ -4,6 +4,8 @@ namespace WizdomNetworks\WizeWeb\Utils;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use WizdomNetworks\WizeWeb\Utils\Logger;
use WizdomNetworks\WizeWeb\Utils\ErrorHandler;
/**
* Mailer Utility
@ -28,8 +30,14 @@ class Mailer
*/
public function __construct()
{
$this->mailer = new PHPMailer(true);
$this->configure();
try {
$this->mailer = new PHPMailer(true);
$this->configure();
} catch (Exception $e) {
Logger::error('Failed to initialize Mailer: ' . $e->getMessage());
ErrorHandler::exception($e);
throw $e;
}
}
/**
@ -41,15 +49,21 @@ class Mailer
*/
protected function configure(): void
{
$this->mailer->isSMTP();
$this->mailer->Host = getenv('MAIL_HOST');
$this->mailer->SMTPAuth = true;
$this->mailer->Username = getenv('MAIL_USER');
$this->mailer->Password = getenv('MAIL_PASSWORD');
$this->mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$this->mailer->Port = (int) getenv('MAIL_PORT');
$this->mailer->setFrom(getenv('MAIL_FROM_EMAIL'), getenv('MAIL_FROM_NAME'));
Logger::info('Mailer configured successfully.');
try {
$this->mailer->isSMTP();
$this->mailer->Host = getenv('MAIL_HOST');
$this->mailer->SMTPAuth = true;
$this->mailer->Username = getenv('MAIL_USER');
$this->mailer->Password = getenv('MAIL_PASSWORD');
$this->mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$this->mailer->Port = (int) getenv('MAIL_PORT');
$this->mailer->setFrom(getenv('MAIL_FROM_EMAIL'), getenv('MAIL_FROM_NAME'));
Logger::info('Mailer configured successfully.');
} catch (Exception $e) {
Logger::error('Mailer configuration failed: ' . $e->getMessage());
ErrorHandler::exception($e);
throw $e;
}
}
/**
@ -65,20 +79,18 @@ class Mailer
public function send(string $to, string $subject, string $body, string $altBody = ''): bool
{
try {
$this->mailer->clearAddresses();
$this->mailer->addAddress($to);
$this->mailer->Subject = $subject;
$this->mailer->Body = $body;
$this->mailer->AltBody = $altBody;
if ($this->mailer->send()) {
Logger::info("Email sent successfully to $to with subject: $subject.");
return true;
} else {
Logger::error("Failed to send email to $to with subject: $subject.");
return false;
}
$this->mailer->send();
Logger::info("Email sent successfully to $to with subject: $subject.");
return true;
} catch (Exception $e) {
Logger::error("Mailer error while sending email to $to: " . $e->getMessage());
Logger::error("Failed to send email to $to: " . $e->getMessage());
ErrorHandler::exception($e);
return false;
}
}

View File

@ -2,97 +2,128 @@
namespace WizdomNetworks\WizeWeb\Utils;
use WizdomNetworks\WizeWeb\Utils\Logger;
use WizdomNetworks\WizeWeb\Utils\ErrorHandler;
/**
* NamespaceUpdater Utility
*
* Provides functionality to update namespaces across PHP files within a given directory.
*/
class NamespaceUpdater
{
/**
* Update namespaces in PHP files within a directory.
*
* @param string $basePath The base directory to scan for PHP files.
* @param string|null $autoloadNamespace The PSR-4 autoload namespace (optional).
*/
public function updateNamespaces(string $basePath, ?string $autoloadNamespace = null): void
{
// Ensure path ends with a directory separator
$basePath = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
// Determine namespace base from composer.json or use default
$namespaceBase = $autoloadNamespace ?? $this->getNamespaceFromComposer($basePath) ?? 'App\\';
$namespaceBase = rtrim(str_replace('\\', '\\', $namespaceBase), '\\') . '\\';
$namespaceBase = rtrim($namespaceBase, '\\') . '\\';
$files = $this->findPhpFiles($basePath);
foreach ($files as $file) {
$this->updateNamespace($file, $namespaceBase, $basePath);
try {
$phpFiles = $this->findPhpFiles($basePath);
foreach ($phpFiles as $filePath) {
$this->updateNamespace($filePath, $namespaceBase, $basePath);
}
Logger::info("Namespace update complete.");
} catch (\Throwable $e) {
Logger::error("Failed to update namespaces: " . $e->getMessage());
ErrorHandler::exception($e);
}
echo "Namespace update complete.\n";
}
/**
* Retrieve the PSR-4 namespace from composer.json.
*
* @param string $basePath The base path of the project.
* @return string|null The namespace or null if not found.
*/
private function getNamespaceFromComposer(string $basePath): ?string
{
$composerPath = $basePath . 'composer.json';
try {
$composerPath = $basePath . 'composer.json';
if (!file_exists($composerPath)) {
echo "composer.json not found. Using default namespace.\n";
if (!file_exists($composerPath)) {
Logger::warning("composer.json not found. Using default namespace.");
return null;
}
$composerConfig = json_decode(file_get_contents($composerPath), true);
return $composerConfig['autoload']['psr-4'][array_key_first($composerConfig['autoload']['psr-4'])] ?? null;
} catch (\Throwable $e) {
Logger::error("Failed to read composer.json: " . $e->getMessage());
ErrorHandler::exception($e);
return null;
}
$composerConfig = json_decode(file_get_contents($composerPath), true);
if (
isset($composerConfig['autoload']['psr-4']) &&
is_array($composerConfig['autoload']['psr-4'])
) {
$namespaces = array_keys($composerConfig['autoload']['psr-4']);
return $namespaces[0] ?? null;
}
echo "No PSR-4 autoload namespace found in composer.json. Using default namespace.\n";
return null;
}
/**
* Find all PHP files in a directory, excluding the vendor directory.
*
* @param string $directory The directory to scan.
* @return array The list of PHP file paths.
*/
private function findPhpFiles(string $directory): array
{
$phpFiles = [];
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory));
$files = [];
try {
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory));
foreach ($iterator as $file) {
if ($file->getExtension() === 'php' && strpos($file->getPathname(), DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR) === false) {
$phpFiles[] = $file->getPathname();
foreach ($iterator as $file) {
if ($file->getExtension() === 'php' && strpos($file->getPathname(), DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR) === false) {
$files[] = $file->getPathname();
}
}
} catch (\Throwable $e) {
Logger::error("Failed to find PHP files in directory: $directory | Error: " . $e->getMessage());
ErrorHandler::exception($e);
}
return $phpFiles;
return $files;
}
/**
* Update the namespace in a specific PHP file.
*
* @param string $filePath The file to update.
* @param string $namespaceBase The base namespace to use.
* @param string $basePath The base path of the project.
*/
private function updateNamespace(string $filePath, string $namespaceBase, string $basePath): void
{
// Get the relative path from the base directory
$relativePath = str_replace($basePath, '', $filePath);
{
try {
$relativePath = str_replace($basePath, '', $filePath);
// Remove the 'app/' prefix from the relative path
if (strpos($relativePath, 'app/') === 0) {
$relativePath = substr($relativePath, strlen('app/'));
if (strpos($relativePath, 'app/') === 0) {
$relativePath = substr($relativePath, strlen('app/'));
}
$namespaceParts = explode(DIRECTORY_SEPARATOR, dirname($relativePath));
$namespace = $namespaceBase . implode('\\', $namespaceParts);
$content = file_get_contents($filePath);
if (!preg_match('/^namespace\s+[^;]+;/m', $content)) {
Logger::info("Skipping file without namespace: $filePath");
return;
}
if (preg_match('/^namespace\s+' . preg_quote($namespace, '/') . ';$/m', $content)) {
Logger::info("Namespace already correct in file: $filePath");
return;
}
$content = preg_replace('/^namespace\s+[^;]+;/m', "namespace $namespace;", $content);
file_put_contents($filePath, $content);
Logger::info("Updated namespace in file: $filePath");
} catch (\Throwable $e) {
Logger::error("Failed to update namespace in file: $filePath | Error: " . $e->getMessage());
ErrorHandler::exception($e);
}
}
// Build the namespace
$namespaceParts = explode(DIRECTORY_SEPARATOR, dirname($relativePath));
$namespace = $namespaceBase . implode('\\', $namespaceParts);
// Get the file content
$content = file_get_contents($filePath);
// Skip files without a namespace declaration
if (!preg_match('/^namespace\s+[^;]+;/m', $content)) {
echo "Skipping file without namespace: $filePath\n";
return;
}
// Skip if the namespace is already correct
if (preg_match('/^namespace\s+' . preg_quote($namespace, '/') . ';$/m', $content)) {
echo "Namespace already correct in file: $filePath\n";
return;
}
// Replace or add the namespace
$content = preg_replace('/^namespace\s+[^;]+;/m', "namespace $namespace;", $content);
// Save the updated file
file_put_contents($filePath, $content);
echo "Updated namespace in file: $filePath\n";
}
}

View File

@ -2,11 +2,13 @@
namespace WizdomNetworks\WizeWeb\Utils;
use WizdomNetworks\WizeWeb\Utils\Logger;
use WizdomNetworks\WizeWeb\Utils\ErrorHandler;
/**
* Sanitizer Utility
*
* Provides methods for sanitizing various types of data, including strings, emails, URLs, and arrays.
*
* Logs sanitized data for debugging and traceability.
*/
class Sanitizer
@ -19,9 +21,15 @@ class Sanitizer
*/
public static function sanitizeString(string $value): string
{
$sanitized = filter_var($value, FILTER_SANITIZE_STRING);
Logger::info("Sanitized string: Original: $value | Sanitized: $sanitized");
return $sanitized;
try {
$sanitized = filter_var($value, FILTER_SANITIZE_STRING);
Logger::info("Sanitized string: Original: $value | Sanitized: $sanitized");
return $sanitized;
} catch (\Throwable $e) {
Logger::error("Failed to sanitize string: $value");
ErrorHandler::exception($e);
return '';
}
}
/**
@ -32,9 +40,15 @@ class Sanitizer
*/
public static function sanitizeEmail(string $value): string
{
$sanitized = filter_var($value, FILTER_SANITIZE_EMAIL);
Logger::info("Sanitized email: Original: $value | Sanitized: $sanitized");
return $sanitized;
try {
$sanitized = filter_var($value, FILTER_SANITIZE_EMAIL);
Logger::info("Sanitized email: Original: $value | Sanitized: $sanitized");
return $sanitized;
} catch (\Throwable $e) {
Logger::error("Failed to sanitize email: $value");
ErrorHandler::exception($e);
return '';
}
}
/**
@ -45,9 +59,15 @@ class Sanitizer
*/
public static function sanitizeURL(string $value): string
{
$sanitized = filter_var($value, FILTER_SANITIZE_URL);
Logger::info("Sanitized URL: Original: $value | Sanitized: $sanitized");
return $sanitized;
try {
$sanitized = filter_var($value, FILTER_SANITIZE_URL);
Logger::info("Sanitized URL: Original: $value | Sanitized: $sanitized");
return $sanitized;
} catch (\Throwable $e) {
Logger::error("Failed to sanitize URL: $value");
ErrorHandler::exception($e);
return '';
}
}
/**
@ -58,8 +78,14 @@ class Sanitizer
*/
public static function sanitizeArray(array $values): array
{
$sanitizedArray = filter_var_array($values, FILTER_SANITIZE_STRING);
Logger::info("Sanitized array: Original: " . json_encode($values) . " | Sanitized: " . json_encode($sanitizedArray));
return $sanitizedArray;
try {
$sanitizedArray = filter_var_array($values, FILTER_SANITIZE_STRING);
Logger::info("Sanitized array: Original: " . json_encode($values) . " | Sanitized: " . json_encode($sanitizedArray));
return $sanitizedArray;
} catch (\Throwable $e) {
Logger::error("Failed to sanitize array: " . json_encode($values));
ErrorHandler::exception($e);
return [];
}
}
}

View File

@ -2,6 +2,14 @@
namespace WizdomNetworks\WizeWeb\Utils;
use WizdomNetworks\WizeWeb\Utils\Logger;
use WizdomNetworks\WizeWeb\Utils\ErrorHandler;
/**
* StructureGenerator Utility
*
* Handles the creation of file and directory structures for the application.
*/
class StructureGenerator
{
private array $structure = [
@ -88,31 +96,41 @@ class StructureGenerator
public function createStructure(string $basePath): void
{
foreach ($this->structure as $folder) {
$path = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $folder;
if (!is_dir($path)) {
mkdir($path, 0755, true);
echo "Created directory: $path\n";
} else {
echo "Directory already exists: $path\n";
try {
$path = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $folder;
if (!is_dir($path)) {
mkdir($path, 0755, true);
Logger::info("Created directory: $path");
} else {
Logger::info("Directory already exists: $path");
}
} catch (\Throwable $e) {
Logger::error("Failed to create directory: $folder");
ErrorHandler::exception($e);
}
}
foreach ($this->files as $file) {
$filePath = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file;
if (!file_exists($filePath)) {
file_put_contents($filePath, $this->getDefaultContent($file));
echo "Created file with default content: $filePath\n";
} else {
$currentContent = file_get_contents($filePath);
$defaultContent = $this->getDefaultContent($file);
if (trim($currentContent) === '') {
file_put_contents($filePath, $defaultContent);
echo "Updated empty file with default content: $filePath\n";
} elseif (trim($currentContent) === trim($defaultContent)) {
echo "File already has default content, no changes made: $filePath\n";
try {
$filePath = rtrim($basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $file;
if (!file_exists($filePath)) {
file_put_contents($filePath, $this->getDefaultContent($file));
Logger::info("Created file with default content: $filePath");
} else {
echo "File has additional content, no changes made: $filePath\n";
$currentContent = file_get_contents($filePath);
$defaultContent = $this->getDefaultContent($file);
if (trim($currentContent) === '') {
file_put_contents($filePath, $defaultContent);
Logger::info("Updated empty file with default content: $filePath");
} elseif (trim($currentContent) === trim($defaultContent)) {
Logger::info("File already has default content, no changes made: $filePath");
} else {
Logger::info("File has additional content, no changes made: $filePath");
}
}
} catch (\Throwable $e) {
Logger::error("Failed to create or update file: $file");
ErrorHandler::exception($e);
}
}
}
@ -168,4 +186,3 @@ if (PHP_SAPI === 'cli') {
$generator = new StructureGenerator();
$generator->createStructure($path);
}
?>

View File

@ -2,6 +2,9 @@
namespace WizdomNetworks\WizeWeb\Utils;
use WizdomNetworks\WizeWeb\Utils\Logger;
use WizdomNetworks\WizeWeb\Utils\ErrorHandler;
/**
* Validator Utility
*
@ -10,17 +13,17 @@ namespace WizdomNetworks\WizeWeb\Utils;
class Validator
{
/**
* Check if a value is not empty.
* Check if a value is non-empty.
*
* @param mixed $value The value to check.
* @param string $value The value to check.
* @return bool True if the value is not empty, false otherwise.
*/
public static function isRequired($value): bool
public static function isRequired(string $value): bool
{
try {
Logger::info("[DEBUG] Checking if value is required: $value");
$isValid = !empty($value);
$isValid = !empty(trim($value));
if (!$isValid) {
Logger::warning("[WARNING] Value is required but empty.");
@ -118,7 +121,7 @@ class Validator
try {
Logger::info("[DEBUG] Checking if string has minimum length: $minLength");
$isValid = strlen($string) >= $minLength;
$isValid = strlen(trim($string)) >= $minLength;
if (!$isValid) {
Logger::warning("[WARNING] String is shorter than minimum length: $string");
@ -143,7 +146,7 @@ class Validator
try {
Logger::info("[DEBUG] Checking if string has maximum length: $maxLength");
$isValid = strlen($string) <= $maxLength;
$isValid = strlen(trim($string)) <= $maxLength;
if (!$isValid) {
Logger::warning("[WARNING] String exceeds maximum length: $string");