📄 functions.php
📁 Path: //home7/skhata/excellenteducation.com.np/includes/functions.php
📊 Size: 8.37 KB
🔒 Perm: 0644
📝 MIME: text/x-php
<?php
require_once __DIR__ . '/../config/db.php';
/** Fetch the single settings row (cached per request). */
function get_settings(): array {
static $settings = null;
if ($settings === null) {
$stmt = db()->query('SELECT * FROM settings WHERE id = 1');
$settings = $stmt->fetch() ?: [];
}
return $settings;
}
/**
* Write (or rewrite) a thin clean-URL stub file at the site root, e.g.
* study-abroad-australia.php, that simply forwards to the shared dynamic
* template with the right slug. Called after saving a country / program /
* blog post so admin-added content gets a real, crawlable clean URL without
* needing mod_rewrite. If the slug changed, pass $oldSlug to remove the
* previous stub file.
*/
function write_route_stub(string $prefix, string $slug, string $targetFile, ?string $oldSlug = null): void {
$root = __DIR__ . '/..';
if ($oldSlug && $oldSlug !== $slug) {
$oldPath = $root . '/' . $prefix . $oldSlug . '.php';
if (is_file($oldPath)) @unlink($oldPath);
}
$path = $root . '/' . $prefix . $slug . '.php';
$content = "<?php\n// Auto-generated clean-URL route. Do not edit directly - managed by the admin panel.\n\$_GET['slug'] = " . var_export($slug, true) . ";\nrequire __DIR__ . '/" . $targetFile . "';\n";
file_put_contents($path, $content);
}
/** Convenience getter for one setting key with fallback. */
function setting(string $key, string $default = ''): string {
$s = get_settings();
return $s[$key] ?? $default;
}
/** Turn arbitrary text into a URL-safe slug. */
function slugify(string $text): string {
$text = preg_replace('~[^\pL\d]+~u', '-', $text);
$text = trim($text, '-');
$text = iconv('utf-8', 'ascii//TRANSLIT', $text) ?: $text;
$text = strtolower($text);
$text = preg_replace('~[^-\w]+~', '', $text);
return $text ?: 'n-a';
}
/** Escape helper for output in templates. */
function e(?string $value): string {
return htmlspecialchars($value ?? '', ENT_QUOTES, 'UTF-8');
}
/** Convert stored newlines into paragraph tags for long-text fields. */
function render_paragraphs(?string $text): string {
$text = trim((string) $text);
if ($text === '') return '';
$blocks = preg_split('/\r\n\r\n|\n\n/', $text);
$html = '';
foreach ($blocks as $block) {
$block = trim($block);
if ($block === '') continue;
$html .= '<p class="text-gray-500 dark:text-gray-400 leading-relaxed mb-4">' . nl2br(e($block)) . '</p>' . "\n";
}
return $html;
}
/** Handle an uploaded image; returns the stored relative path or null. */
function handle_image_upload(string $field, string $subfolder, ?string $existing = null): ?string {
if (empty($_FILES[$field]) || $_FILES[$field]['error'] === UPLOAD_ERR_NO_FILE) {
return $existing;
}
if ($_FILES[$field]['error'] !== UPLOAD_ERR_OK) {
return $existing;
}
$allowed = ['jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp', 'gif' => 'image/gif'];
$ext = strtolower(pathinfo($_FILES[$field]['name'], PATHINFO_EXTENSION));
if (!array_key_exists($ext, $allowed)) {
return $existing;
}
$dir = __DIR__ . '/../uploads/' . trim($subfolder, '/');
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
$filename = uniqid($subfolder . '_', true) . '.' . $ext;
$dest = $dir . '/' . $filename;
if (move_uploaded_file($_FILES[$field]['tmp_name'], $dest)) {
return 'uploads/' . trim($subfolder, '/') . '/' . $filename;
}
return $existing;
}
/** Resolve an image value into a full <img> src: absolute URL, Unsplash photo id, or local upload path. */
function resolve_image(?string $value, string $fallback = ''): string {
$value = trim((string) $value);
if ($value === '') return $fallback;
if (str_starts_with($value, 'http://') || str_starts_with($value, 'https://')) return $value;
if (str_starts_with($value, 'photo-')) return 'https://images.unsplash.com/' . $value . '?q=80&w=900';
if (str_starts_with($value, 'uploads/') || str_starts_with($value, 'assets/')) return SITE_URL . '/' . $value;
return $value;
}
/** Simple CSRF token helpers. */
function csrf_token(): string {
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function csrf_field(): string {
return '<input type="hidden" name="csrf_token" value="' . e(csrf_token()) . '">';
}
function csrf_verify(): bool {
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$token = $_POST['csrf_token'] ?? '';
return !empty($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
function redirect(string $path): void {
header('Location: ' . $path);
exit;
}
function flash_set(string $type, string $message): void {
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$_SESSION['flash'] = ['type' => $type, 'message' => $message];
}
function flash_get(): ?array {
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['flash'])) return null;
$f = $_SESSION['flash'];
unset($_SESSION['flash']);
return $f;
}
/** Decode a JSON column safely to an array. */
function jd($value): array {
if (is_array($value)) return $value;
$decoded = json_decode((string) $value, true);
return is_array($decoded) ? $decoded : [];
}
/** Turn a textarea (one item per line) into a JSON array string for storage. */
function lines_to_json(string $text): string {
$lines = preg_split('/\r\n|\r|\n/', $text);
$lines = array_values(array_filter(array_map('trim', $lines), fn($l) => $l !== ''));
return json_encode($lines, JSON_UNESCAPED_UNICODE);
}
/** Turn a JSON array column back into a newline-separated textarea value. */
function json_to_lines($value): string {
return implode("\n", jd($value));
}
/**
* Turn a textarea of "icon|amount|label" lines into a JSON array of objects
* for the cost_items column, e.g. "fa-house|AUD 1,400-2,500/mo|Accommodation".
*/
function cost_lines_to_json(string $text): string {
$lines = preg_split('/\r\n|\r|\n/', $text);
$items = [];
foreach ($lines as $line) {
$line = trim($line);
if ($line === '') continue;
$parts = array_map('trim', explode('|', $line));
$items[] = ['icon' => $parts[0] ?? 'fa-circle', 'amount' => $parts[1] ?? '', 'label' => $parts[2] ?? ''];
}
return json_encode($items, JSON_UNESCAPED_UNICODE);
}
/** Turn the cost_items JSON column back into "icon|amount|label" textarea lines. */
function json_to_cost_lines($value): string {
$items = jd($value);
$lines = [];
foreach ($items as $it) {
$lines[] = ($it['icon'] ?? '') . '|' . ($it['amount'] ?? '') . '|' . ($it['label'] ?? '');
}
return implode("\n", $lines);
}
/**
* Turn a textarea of "Batch Name|Days|Time" lines into JSON for the test
* preparation schedule column, e.g. "Morning Batch|Sun-Fri|7:00 - 8:30 AM".
*/
function schedule_lines_to_json(string $text): string {
$lines = preg_split('/\r\n|\r|\n/', $text);
$items = [];
foreach ($lines as $line) {
$line = trim($line);
if ($line === '') continue;
$parts = array_map('trim', explode('|', $line));
$items[] = ['batch' => $parts[0] ?? '', 'days' => $parts[1] ?? '', 'time' => $parts[2] ?? ''];
}
return json_encode($items, JSON_UNESCAPED_UNICODE);
}
function json_to_schedule_lines($value): string {
$items = jd($value);
$lines = [];
foreach ($items as $it) {
$lines[] = ($it['batch'] ?? '') . '|' . ($it['days'] ?? '') . '|' . ($it['time'] ?? '');
}
return implode("\n", $lines);
}
/**
* Turn a textarea of "Question|Answer" lines into JSON for FAQ-style columns.
*/
function faq_lines_to_json(string $text): string {
$lines = preg_split('/\r\n|\r|\n/', $text);
$items = [];
foreach ($lines as $line) {
$line = trim($line);
if ($line === '') continue;
$parts = array_map('trim', explode('|', $line, 2));
$items[] = ['q' => $parts[0] ?? '', 'a' => $parts[1] ?? ''];
}
return json_encode($items, JSON_UNESCAPED_UNICODE);
}
function json_to_faq_lines($value): string {
$items = jd($value);
$lines = [];
foreach ($items as $it) {
$lines[] = ($it['q'] ?? '') . '|' . ($it['a'] ?? '');
}
return implode("\n", $lines);
}