Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
8d0660a262 yu 2025-11-02 07:44:15 +00:00
45 changed files with 4624 additions and 141 deletions

145
admin_dashboard.php Normal file
View File

@ -0,0 +1,145 @@
<?php
session_start();
if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'admin') {
header('Location: index.php');
exit;
}
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/includes/header.php';
$leave_requests = [];
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT leave_requests.*, users.full_name AS student_name FROM leave_requests JOIN users ON leave_requests.student_id = users.id WHERE leave_requests.status = 'approved_by_teacher' ORDER BY leave_requests.created_at DESC");
$stmt->execute();
$leave_requests = $stmt->fetchAll();
} catch (PDOException $e) {
// handle error
}
// Monthly report
$report = [];
$month = date('Y-m');
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT status, COUNT(*) as count FROM leave_requests WHERE DATE_FORMAT(created_at, '%Y-%m') = ? GROUP BY status");
$stmt->execute([$month]);
$result = $stmt->fetchAll();
foreach ($result as $row) {
$report[$row['status']] = $row['count'];
}
} catch (PDOException $e) {
// handle error
}
$import_success_message = '';
if (isset($_GET['import_success']) && $_GET['import_success'] == 1) {
$import_success_message = 'Students imported successfully!';
}
$import_error_message = '';
if (isset($_GET['import_error']) && $_GET['import_error'] == 1) {
$import_error_message = 'Error importing students. Please check the file and try again.';
}
?>
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-3">
<h1>Admin Dashboard</h1>
<div>
<span class="me-3">Welcome, <?php echo htmlspecialchars($_SESSION['user_full_name']); ?>!</span>
<a href="logout.php" class="btn btn-primary">Logout</a>
</div>
</div>
<?php if ($import_success_message): ?>
<div class="alert alert-success" role="alert">
<?php echo $import_success_message; ?>
</div>
<?php endif; ?>
<?php if ($import_error_message): ?>
<div class="alert alert-danger" role="alert">
<?php echo $import_error_message; ?>
</div>
<?php endif; ?>
<div class="row mt-4">
<div class="col-md-12">
<h2>Pending Leave Requests for Final Approval</h2>
<table class="table">
<thead>
<tr>
<th>Student Name</th>
<th>Leave Type</th>
<th>Start Date</th>
<th>End Date</th>
<th>Reason</th>
<th>Attachment</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($leave_requests as $request): ?>
<tr>
<td><?php echo htmlspecialchars($request['student_name']); ?></td>
<td><?php echo htmlspecialchars($request['leave_type']); ?></td>
<td><?php echo htmlspecialchars($request['start_date']); ?></td>
<td><?php echo htmlspecialchars($request['end_date']); ?></td>
<td><?php echo htmlspecialchars($request['reason']); ?></td>
<td>
<?php if ($request['attachment_path']): ?>
<a href="<?php echo htmlspecialchars($request['attachment_path']); ?>" target="_blank">View Attachment</a>
<?php else: ?>
No Attachment
<?php endif; ?>
</td>
<td>
<a href="update_leave_status.php?id=<?php echo $request['id']; ?>&status=approved_by_admin" class="btn btn-success btn-sm">Approve</a>
<a href="update_leave_status.php?id=<?php echo $request['id']; ?>&status=rejected_by_admin" class="btn btn-danger btn-sm">Reject</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="row mt-4">
<div class="col-md-6">
<h2>Monthly Report (<?php echo date('F Y'); ?>)</h2>
<table class="table">
<thead>
<tr>
<th>Status</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<?php foreach ($report as $status => $count): ?>
<tr>
<td><?php echo htmlspecialchars($status); ?></td>
<td><?php echo $count; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<a href="export_report.php" class="btn btn-primary">Export to CSV</a>
</div>
<div class="col-md-6">
<h2>Import Students</h2>
<form action="import_students.php" method="POST" enctype="multipart/form-data">
<div class="mb-3">
<label for="student_file" class="form-label">Select CSV file</label>
<input type="file" class="form-control" id="student_file" name="student_file" accept=".csv" required>
</div>
<button type="submit" class="btn btn-primary">Import Students</button>
</form>
</div>
</div>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

39
assets/css/style.css Normal file
View File

@ -0,0 +1,39 @@
html, body {
height: 100%;
}
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}

150
blog.php Normal file
View File

@ -0,0 +1,150 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My Awesome App</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
</head>
<body>
<?php include 'includes/header.php'; ?>
<div class="container">
<div class="row">
<div class="col-md-8">
<h1 class="my-4">Page Heading
<small>Secondary Text</small>
</h1>
<!-- Blog Post -->
<div class="card mb-4">
<img class="card-img-top" src="http://placehold.it/750x300" alt="Card image cap">
<div class="card-body">
<h2 class="card-title">Post Title</h2>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis aliquid atque, nulla? Quos cum ex quis soluta, a laboriosam. Dicta expedita corporis animi vero voluptate voluptatibus possimus, veniam magni quis!</p>
<a href="#" class="btn btn-primary">Read More &rarr;</a>
</div>
<div class="card-footer text-muted">
Posted on January 1, 2020 by
<a href="#">Start Bootstrap</a>
</div>
</div>
<!-- Blog Post -->
<div class="card mb-4">
<img class="card-img-top" src="http://placehold.it/750x300" alt="Card image cap">
<div class="card-body">
<h2 class="card-title">Post Title</h2>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis aliquid atque, nulla? Quos cum ex quis soluta, a laboriosam. Dicta expedita corporis animi vero voluptate voluptatibus possimus, veniam magni quis!</p>
<a href="#" class="btn btn-primary">Read More &rarr;</a>
</div>
<div class="card-footer text-muted">
Posted on January 1, 2020 by
<a href="#">Start Bootstrap</a>
</div>
</div>
<!-- Blog Post -->
<div class="card mb-4">
<img class="card-img-top" src="http://placehold.it/750x300" alt="Card image cap">
<div class="card-body">
<h2 class="card-title">Post Title</h2>
<p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis aliquid atque, nulla? Quos cum ex quis soluta, a laboriosam. Dicta expedita corporis animi vero voluptate voluptatibus possimus, veniam magni quis!</p>
<a href="#" class="btn btn-primary">Read More &rarr;</a>
</div>
<div class="card-footer text-muted">
Posted on January 1, 2020 by
<a href="#">Start Bootstrap</a>
</div>
</div>
<!-- Pagination -->
<ul class="pagination justify-content-center mb-4">
<li class="page-item">
<a class="page-link" href="#">&larr; Older</a>
</li>
<li class="page-item disabled">
<a class="page-link" href="#">Newer &rarr;</a>
</li>
</ul>
</div>
<!-- Sidebar Widgets Column -->
<div class="col-md-4">
<!-- Search Widget -->
<div class="card my-4">
<h5 class="card-header">Search</h5>
<div class="card-body">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search for...">
<span class="input-group-btn">
<button class="btn btn-secondary" type="button">Go!</button>
</span>
</div>
</div>
</div>
<!-- Categories Widget -->
<div class="card my-4">
<h5 class="card-header">Categories</h5>
<div class="card-body">
<div class="row">
<div class="col-lg-6">
<ul class="list-unstyled mb-0">
<li>
<a href="#">Web Design</a>
</li>
<li>
<a href="#">HTML</a>
</li>
<li>
<a href="#">Freebies</a>
</li>
</ul>
</div>
<div class="col-lg-6">
<ul class="list-unstyled mb-0">
<li>
<a href="#">JavaScript</a>
</li>
<li>
<a href="#">CSS</a>
</li>
<li>
<a href="#">Tutorials</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Side Widget -->
<div class="card my-4">
<h5 class="card-header">Side Widget</h5>
<div class="card-body">
You can put anything you want inside of these side widgets. They are easy to use, and feature the new Bootstrap 4 card containers!
</div>
</div>
</div>
</div>
<!-- /.row -->
</div>
<?php include 'includes/footer.php'; ?>
</body>
</html>

5
composer.json Normal file
View File

@ -0,0 +1,5 @@
{
"require": {
"parsecsv/php-parsecsv": "^1.3"
}
}

83
composer.lock generated Normal file
View File

@ -0,0 +1,83 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "710967b1d9d7c2e29226ea5c1834b93a",
"packages": [
{
"name": "parsecsv/php-parsecsv",
"version": "1.3.2",
"source": {
"type": "git",
"url": "https://github.com/parsecsv/parsecsv-for-php.git",
"reference": "2d6236cae09133e0533d34ed45ba1e1ecafffebb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/parsecsv/parsecsv-for-php/zipball/2d6236cae09133e0533d34ed45ba1e1ecafffebb",
"reference": "2d6236cae09133e0533d34ed45ba1e1ecafffebb",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "^6",
"squizlabs/php_codesniffer": "^3.5"
},
"suggest": {
"illuminate/support": "Fluent array interface for map functions"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"ParseCsv\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Myhrberg",
"email": "contact@jimeh.me"
},
{
"name": "William Knauss",
"email": "will.knauss@gmail.com"
},
{
"name": "Susann Sgorzaly",
"homepage": "https://github.com/susgo"
},
{
"name": "Christian Bläul",
"homepage": "https://github.com/Fonata"
}
],
"description": "CSV data parser for PHP",
"support": {
"issues": "https://github.com/parsecsv/parsecsv-for-php/issues",
"source": "https://github.com/parsecsv/parsecsv-for-php"
},
"time": "2021-11-07T14:15:46+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

BIN
composer.phar Executable file

Binary file not shown.

18
cron/send_reminders.php Normal file
View File

@ -0,0 +1,18 @@
<?php
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../mail/MailService.php';
// Get all pending leave requests older than 24 hours
$pdo = db();
$stmt = $pdo->prepare("SELECT leave_requests.*, users.full_name AS student_name FROM leave_requests JOIN users ON leave_requests.student_id = users.id WHERE status = 'pending' AND created_at < NOW() - INTERVAL 24 HOUR");
$stmt->execute();
$requests = $stmt->fetchAll();
foreach ($requests as $request) {
// Send reminder email to teacher
$teacher_email = 'teacher@example.com'; // Hardcoded for now
$subject = 'Reminder: Pending Leave Request from ' . $request['student_name'];
$body = "<p>This is a reminder that a leave request from {$request['student_name']} is still pending your approval.</p>\n <p>Please login to the dashboard to review the request.</p>";
MailService::sendMail($teacher_email, $subject, $body);
}

View File

@ -0,0 +1,51 @@
-- Create users table
CREATE TABLE IF NOT EXISTS `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`email` VARCHAR(255) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL,
`role` ENUM('student', 'teacher', 'admin') NOT NULL,
`full_name` VARCHAR(255) DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Create leave_requests table
CREATE TABLE IF NOT EXISTS `leave_requests` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`student_id` INT NOT NULL,
`start_date` DATE NOT NULL,
`end_date` DATE NOT NULL,
`reason` TEXT,
`status` ENUM('pending_teacher', 'pending_admin', 'approved', 'rejected') NOT NULL DEFAULT 'pending_teacher',
`teacher_id` INT DEFAULT NULL,
`teacher_approved_at` DATETIME DEFAULT NULL,
`teacher_rejection_reason` TEXT DEFAULT NULL,
`admin_id` INT DEFAULT NULL,
`admin_approved_at` DATETIME DEFAULT NULL,
`admin_rejection_reason` TEXT DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`student_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`teacher_id`) REFERENCES `users`(`id`) ON DELETE SET NULL,
FOREIGN KEY (`admin_id`) REFERENCES `users`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Create leave_request_attachments table
CREATE TABLE IF NOT EXISTS `leave_request_attachments` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`leave_request_id` INT NOT NULL,
`file_path` VARCHAR(255) NOT NULL,
`original_filename` VARCHAR(255) DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`leave_request_id`) REFERENCES `leave_requests`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Create password_resets table
CREATE TABLE IF NOT EXISTS `password_resets` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`email` VARCHAR(255) NOT NULL,
`token` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS `password_resets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`token` varchar(255) NOT NULL,
`expires_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `token` (`token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@ -0,0 +1,13 @@
CREATE TABLE IF NOT EXISTS `leave_requests` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) NOT NULL,
`leave_type` varchar(255) NOT NULL,
`start_date` date NOT NULL,
`end_date` date NOT NULL,
`reason` text NOT NULL,
`attachment_path` varchar(255) DEFAULT NULL,
`status` varchar(255) NOT NULL DEFAULT 'pending',
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
KEY `student_id` (`student_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

41
export_report.php Normal file
View File

@ -0,0 +1,41 @@
<?php
session_start();
if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'admin') {
header('Location: index.php');
exit;
}
require_once __DIR__ . '/db/config.php';
$month = date('Y-m');
$filename = "leave_report_{$month}.csv";
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '"');
$output = fopen('php://output', 'w');
fputcsv($output, ['Student Name', 'Leave Type', 'Start Date', 'End Date', 'Reason', 'Status']);
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT users.full_name AS student_name, leave_requests.* FROM leave_requests JOIN users ON leave_requests.student_id = users.id WHERE DATE_FORMAT(leave_requests.created_at, '%Y-%m') = ?");
$stmt->execute([$month]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
fputcsv($output, [
$row['student_name'],
$row['leave_type'],
$row['start_date'],
$row['end_date'],
$row['reason'],
$row['status']
]);
}
} catch (PDOException $e) {
// handle error
}
fclose($output);
exit;

75
forgot_password.php Normal file
View File

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/mail/MailService.php';
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'] ?? '';
if (empty($email)) {
$message = 'Please enter your email address.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user) {
// Generate a unique token
$token = bin2hex(random_bytes(32));
// Set expiration date to 1 hour from now
$expires_at = date('Y-m-d H:i:s', strtotime('+1 hour'));
// Store the token in the database
$stmt = $pdo->prepare("INSERT INTO password_resets (email, token, expires_at) VALUES (?, ?, ?)");
$stmt->execute([$email, $token, $expires_at]);
// Send the password reset link
$reset_link = 'http://' . $_SERVER['HTTP_HOST'] . '/reset_password.php?token=' . $token;
// For now, we just display the link. Later we will send it by email.
$message = 'Password reset link: <a href="' . $reset_link . '">' . $reset_link . '</a>';
} else {
$message = 'If your email address exists in our database, you will receive a password reset link.';
}
} catch (PDOException $e) {
$message = 'Database error: ' . $e->getMessage();
}
}
}
require_once __DIR__ . '/includes/header.php';
?>
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<h2>Forgot Password</h2>
<p>Please enter your email address to receive a password reset link.</p>
<?php if ($message): ?>
<div class="alert alert-info" role="alert">
<?php echo $message; ?>
</div>
<?php endif; ?>
<form method="POST">
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<button type="submit" class="btn btn-primary">Send Reset Link</button>
</form>
</div>
</div>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

43
import_students.php Normal file
View File

@ -0,0 +1,43 @@
<?php
session_start();
if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'admin') {
header('Location: index.php');
exit;
}
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/db/config.php';
use ParseCsv\Csv;
if (isset($_FILES['student_file']) && $_FILES['student_file']['error'] === UPLOAD_ERR_OK) {
$csv = new Csv();
$csv->auto($_FILES['student_file']['tmp_name']);
$pdo = db();
foreach ($csv->data as $row) {
$full_name = $row['full_name'];
$email = $row['email'];
// Generate a random password
$password = bin2hex(random_bytes(8));
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
try {
$stmt = $pdo->prepare("INSERT INTO users (full_name, email, password, role) VALUES (?, ?, ?, 'student')");
$stmt->execute([$full_name, $email, $hashed_password]);
} catch (PDOException $e) {
// Handle duplicate email or other errors
// For now, we just skip the row
continue;
}
}
header('Location: admin_dashboard.php?import_success=1');
exit;
} else {
header('Location: admin_dashboard.php?import_error=1');
exit;
}

10
includes/footer.php Normal file
View File

@ -0,0 +1,10 @@
<footer class="py-3 my-4">
<ul class="nav justify-content-center border-bottom pb-3 mb-3">
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">Home</a></li>
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">Features</a></li>
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">Pricing</a></li>
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">FAQs</a></li>
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted">About</a></li>
</ul>
<p class="text-center text-muted">© 2025 Awesome App, Inc</p>
</footer>

10
includes/header.php Normal file
View File

@ -0,0 +1,10 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Login - School Leave System</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>

227
index.php
View File

@ -4,147 +4,92 @@ declare(strict_types=1);
@error_reporting(E_ALL); @error_reporting(E_ALL);
@date_default_timezone_set('UTC'); @date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION; require_once __DIR__ . '/db/config.php';
$now = date('Y-m-d H:i:s');
session_start();
// Redirect if already logged in
if (isset($_SESSION['user_role'])) {
header('Location: ' . $_SESSION['user_role'] . '_dashboard.php');
exit;
}
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
$role = $_POST['role'] ?? '';
if (empty($email) || empty($password) || empty($role)) {
$error_message = 'Please provide email, password, and select a role.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ? AND role = ?");
$stmt->execute([$email, $role]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
// Password is correct, set session variables
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_role'] = $user['role'];
$_SESSION['user_email'] = $user['email'];
$_SESSION['user_full_name'] = $user['full_name'];
// Redirect to the respective dashboard
header('Location: ' . $user['role'] . '_dashboard.php');
exit;
} else {
$error_message = 'Invalid email, password, or role.';
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
}
require_once __DIR__ . '/includes/header.php';
?> ?>
<!doctype html> <body class="text-center">
<html lang="en"> <main class="form-signin">
<head> <form method="POST">
<meta charset="utf-8" /> <h1 class="h3 mb-3 fw-normal">School Leave System</h1>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <p class="mb-4">Please sign in to continue</p>
<title>New Style</title>
<?php <?php if ($error_message): ?>
// Read project preview data from environment <div class="alert alert-danger" role="alert">
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; <?php echo htmlspecialchars($error_message); ?>
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; </div>
?> <?php endif; ?>
<?php if ($projectDescription): ?>
<!-- Meta description --> <div class="form-floating mb-3">
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' /> <input type="email" class="form-control" id="floatingInput" name="email" placeholder="name@example.com" required>
<!-- Open Graph meta tags --> <label for="floatingInput">Email address</label>
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div> </div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> <div class="form-floating mb-3">
<p class="hint">This page will update automatically as the plan is implemented.</p> <input type="password" class="form-control" id="floatingPassword" name="password" placeholder="Password" required>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p> <label for="floatingPassword">Password</label>
</div> </div>
<div class="form-floating mb-3">
<select class="form-select" id="floatingRole" name="role" required>
<option value="" selected disabled>Select your role</option>
<option value="student">Siswa (Student)</option>
<option value="teacher">Wali Kelas (Homeroom Teacher)</option>
<option value="admin">Admin</option>
</select>
<label for="floatingRole">Role</label>
</div>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
<p class="mt-3">
<a href="forgot_password.php">Forgot password?</a>
</p>
<p class="mt-5 mb-3 text-muted">&copy; <?php echo date("Y"); ?>. All rights reserved.</p>
</form>
</main> </main>
<footer> <?php require_once __DIR__ . '/includes/footer.php'; ?>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
</body>
</html>

5
logout.php Normal file
View File

@ -0,0 +1,5 @@
<?php
session_start();
session_destroy();
header('Location: index.php');
exit;

101
reset_password.php Normal file
View File

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
require_once __DIR__ . '/db/config.php';
$token = $_GET['token'] ?? '';
$message = '';
$show_form = false;
if (empty($token)) {
$message = 'Invalid password reset token.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM password_resets WHERE token = ? AND expires_at > NOW()");
$stmt->execute([$token]);
$reset_request = $stmt->fetch();
if ($reset_request) {
$show_form = true;
} else {
$message = 'Invalid or expired password reset token.';
}
} catch (PDOException $e) {
$message = 'Database error: ' . $e->getMessage();
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$password = $_POST['password'] ?? '';
$password_confirm = $_POST['password_confirm'] ?? '';
if (empty($password) || empty($password_confirm)) {
$message = 'Please enter and confirm your new password.';
} elseif ($password !== $password_confirm) {
$message = 'Passwords do not match.';
} else {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM password_resets WHERE token = ? AND expires_at > NOW()");
$stmt->execute([$token]);
$reset_request = $stmt->fetch();
if ($reset_request) {
$email = $reset_request['email'];
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Update user's password
$stmt = $pdo->prepare("UPDATE users SET password = ? WHERE email = ?");
$stmt->execute([$hashed_password, $email]);
// Delete the reset token
$stmt = $pdo->prepare("DELETE FROM password_resets WHERE token = ?");
$stmt->execute([$token]);
$message = 'Your password has been reset successfully. You can now <a href="index.php">login</a> with your new password.';
$show_form = false;
} else {
$message = 'Invalid or expired password reset token.';
}
} catch (PDOException $e) {
$message = 'Database error: ' . $e->getMessage();
}
}
}
require_once __DIR__ . '/includes/header.php';
?>
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<h2>Reset Password</h2>
<?php if ($message): ?>
<div class="alert alert-info" role="alert">
<?php echo $message; ?>
</div>
<?php endif; ?>
<?php if ($show_form): ?>
<form method="POST">
<div class="mb-3">
<label for="password" class="form-label">New Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="password_confirm" class="form-label">Confirm New Password</label>
<input type="password" class="form-control" id="password_confirm" name="password_confirm" required>
</div>
<button type="submit" class="btn btn-primary">Reset Password</button>
</form>
<?php endif; ?>
</div>
</div>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

103
student_dashboard.php Normal file
View File

@ -0,0 +1,103 @@
<?php
session_start();
if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'student') {
header('Location: index.php');
exit;
}
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/includes/header.php';
$success_message = '';
if (isset($_GET['success']) && $_GET['success'] == 1) {
$success_message = 'Leave request submitted successfully!';
}
$leave_requests = [];
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM leave_requests WHERE student_id = ? ORDER BY created_at DESC");
$stmt->execute([$_SESSION['user_id']]);
$leave_requests = $stmt->fetchAll();
} catch (PDOException $e) {
// handle error
}
?>
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-3">
<h1>Student Dashboard</h1>
<div>
<span class="me-3">Welcome, <?php echo htmlspecialchars($_SESSION['user_full_name']); ?>!</span>
<a href="logout.php" class="btn btn-primary">Logout</a>
</div>
</div>
<?php if ($success_message): ?>
<div class="alert alert-success" role="alert">
<?php echo $success_message; ?>
</div>
<?php endif; ?>
<div class="row mt-4">
<div class="col-md-6">
<h2>Submit Leave Request</h2>
<form action="submit_leave_request.php" method="POST" enctype="multipart/form-data">
<div class="mb-3">
<label for="leave_type" class="form-label">Leave Type</label>
<select class="form-select" id="leave_type" name="leave_type" required>
<option value="" selected disabled>Select leave type</option>
<option value="sick_leave">Sick Leave</option>
<option value="vacation">Vacation</option>
<option value="personal">Personal</option>
<option value="other">Other</option>
</select>
</div>
<div class="mb-3">
<label for="start_date" class="form-label">Start Date</label>
<input type="date" class="form-control" id="start_date" name="start_date" required>
</div>
<div class="mb-3">
<label for="end_date" class="form-label">End Date</label>
<input type="date" class="form-control" id="end_date" name="end_date" required>
</div>
<div class="mb-3">
<label for="reason" class="form-label">Reason</label>
<textarea class="form-control" id="reason" name="reason" rows="3" required></textarea>
</div>
<div class="mb-3">
<label for="attachment" class="form-label">Attachment (optional)</label>
<input type="file" class="form-control" id="attachment" name="attachment">
</div>
<button type="submit" class="btn btn-primary">Submit Request</button>
</form>
</div>
<div class="col-md-6">
<h2>My Leave Requests</h2>
<table class="table">
<thead>
<tr>
<th>Leave Type</th>
<th>Start Date</th>
<th>End Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($leave_requests as $request): ?>
<tr>
<td><?php echo htmlspecialchars($request['leave_type']); ?></td>
<td><?php echo htmlspecialchars($request['start_date']); ?></td>
<td><?php echo htmlspecialchars($request['end_date']); ?></td>
<td><?php echo htmlspecialchars($request['status']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

52
submit_leave_request.php Normal file
View File

@ -0,0 +1,52 @@
<?php
session_start();
if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'student') {
header('Location: index.php');
exit;
}
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/mail/MailService.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$student_id = $_SESSION['user_id'];
$leave_type = $_POST['leave_type'] ?? '';
$start_date = $_POST['start_date'] ?? '';
$end_date = $_POST['end_date'] ?? '';
$reason = $_POST['reason'] ?? '';
// Basic validation
if (empty($leave_type) || empty($start_date) || empty($end_date) || empty($reason)) {
die('Please fill all required fields.');
}
$attachment_path = null;
if (isset($_FILES['attachment']) && $_FILES['attachment']['error'] === UPLOAD_ERR_OK) {
$upload_dir = __DIR__ . '/uploads/';
$attachment_name = uniqid() . '-' . basename($_FILES['attachment']['name']);
$attachment_path = $upload_dir . $attachment_name;
if (!move_uploaded_file($_FILES['attachment']['tmp_name'], $attachment_path)) {
die('Failed to upload attachment.');
}
$attachment_path = 'uploads/' . $attachment_name; // Store relative path
}
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO leave_requests (student_id, leave_type, start_date, end_date, reason, attachment_path) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$student_id, $leave_type, $start_date, $end_date, $reason, $attachment_path]);
// Send email to teacher
$teacher_email = 'teacher@example.com'; // Hardcoded for now
$subject = 'New Leave Request from ' . $_SESSION['user_full_name'];
$body = "<p>A new leave request has been submitted by {" . $_SESSION['user_full_name'] . "}.</p>\n <p><b>Leave Type:</b> {" . $leave_type . "}</p>\n <p><b>Start Date:</b> {" . $start_date . "}</p>\n <p><b>End Date:</b> {" . $end_date . "}</p>\n <p><b>Reason:</b> {" . $reason . "}</p>\n <p>Please login to the dashboard to approve or reject this request.</p>";
MailService::sendMail($teacher_email, $subject, $body);
header('Location: student_dashboard.php?success=1');
exit;
} catch (PDOException $e) {
die('Database error: ' . $e->getMessage());
}
}

75
teacher_dashboard.php Normal file
View File

@ -0,0 +1,75 @@
<?php
session_start();
if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'teacher') {
header('Location: index.php');
exit;
}
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/includes/header.php';
$leave_requests = [];
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT leave_requests.*, users.full_name AS student_name FROM leave_requests JOIN users ON leave_requests.student_id = users.id WHERE leave_requests.status = 'pending' ORDER BY leave_requests.created_at DESC");
$stmt->execute();
$leave_requests = $stmt->fetchAll();
} catch (PDOException $e) {
// handle error
}
?>
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-3">
<h1>Teacher Dashboard</h1>
<div>
<span class="me-3">Welcome, <?php echo htmlspecialchars($_SESSION['user_full_name']); ?>!</span>
<a href="logout.php" class="btn btn-primary">Logout</a>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<h2>Pending Leave Requests</h2>
<table class="table">
<thead>
<tr>
<th>Student Name</th>
<th>Leave Type</th>
<th>Start Date</th>
<th>End Date</th>
<th>Reason</th>
<th>Attachment</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($leave_requests as $request): ?>
<tr>
<td><?php echo htmlspecialchars($request['student_name']); ?></td>
<td><?php echo htmlspecialchars($request['leave_type']); ?></td>
<td><?php echo htmlspecialchars($request['start_date']); ?></td>
<td><?php echo htmlspecialchars($request['end_date']); ?></td>
<td><?php echo htmlspecialchars($request['reason']); ?></td>
<td>
<?php if ($request['attachment_path']): ?>
<a href="<?php echo htmlspecialchars($request['attachment_path']); ?>" target="_blank">View Attachment</a>
<?php else: ?>
No Attachment
<?php endif; ?>
</td>
<td>
<a href="update_leave_status.php?id=<?php echo $request['id']; ?>&status=approved_by_teacher" class="btn btn-success btn-sm">Approve</a>
<a href="update_leave_status.php?id=<?php echo $request['id']; ?>&status=rejected_by_teacher" class="btn btn-danger btn-sm">Reject</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

85
update_leave_status.php Normal file
View File

@ -0,0 +1,85 @@
<?php
session_start();
if (!isset($_SESSION['user_role'])) {
header('Location: index.php');
exit;
}
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/mail/MailService.php';
if (isset($_GET['id']) && isset($_GET['status'])) {
$leave_request_id = $_GET['id'];
$new_status = $_GET['status'];
if ($_SESSION['user_role'] === 'teacher') {
$allowed_statuses = ['approved_by_teacher', 'rejected_by_teacher'];
if (!in_array($new_status, $allowed_statuses)) {
die('Invalid status.');
}
} elseif ($_SESSION['user_role'] === 'admin') {
$allowed_statuses = ['approved_by_admin', 'rejected_by_admin'];
if (!in_array($new_status, $allowed_statuses)) {
die('Invalid status.');
}
} else {
die('You are not authorized to perform this action.');
}
try {
$pdo = db();
$stmt = $pdo->prepare("UPDATE leave_requests SET status = ? WHERE id = ?");
$stmt->execute([$new_status, $leave_request_id]);
// Get student email
$stmt = $pdo->prepare("SELECT users.email, users.full_name FROM leave_requests JOIN users ON leave_requests.student_id = users.id WHERE leave_requests.id = ?");
$stmt->execute([$leave_request_id]);
$student = $stmt->fetch();
if ($student) {
$student_email = $student['email'];
$student_name = $student['full_name'];
if ($new_status === 'approved_by_teacher') {
// Notify admin
$admin_email = 'admin@example.com'; // Hardcoded for now
$subject = 'Leave Request Approved by Teacher';
$body = "<p>The leave request for {$student_name} has been approved by the teacher and is waiting for your final approval.</p><p>Please login to the dashboard to review the request.</p>";
MailService::sendMail($admin_email, $subject, $body);
// Notify student
$subject_student = 'Your Leave Request has been updated';
$body_student = "<p>Your leave request has been approved by your teacher and is now pending final approval from the admin.</p>";
MailService::sendMail($student_email, $subject_student, $body_student);
} elseif ($new_status === 'rejected_by_teacher') {
// Notify student
$subject_student = 'Your Leave Request has been updated';
$body_student = "<p>Your leave request has been rejected by your teacher.</p>";
MailService::sendMail($student_email, $subject_student, $body_student);
} elseif ($new_status === 'approved_by_admin') {
// Notify student
$subject_student = 'Your Leave Request has been approved';
$body_student = "<p>Your leave request has been approved by the admin.</p>";
MailService::sendMail($student_email, $subject_student, $body_student);
} elseif ($new_status === 'rejected_by_admin') {
// Notify student
$subject_student = 'Your Leave Request has been rejected';
$body_student = "<p>Your leave request has been rejected by the admin.</p>";
MailService::sendMail($student_email, $subject_student, $body_student);
}
}
if ($_SESSION['user_role'] === 'teacher') {
header('Location: teacher_dashboard.php');
} elseif ($_SESSION['user_role'] === 'admin') {
header('Location: admin_dashboard.php');
}
exit;
} catch (PDOException $e) {
die('Database error: ' . $e->getMessage());
}
}

22
vendor/autoload.php vendored Normal file
View File

@ -0,0 +1,22 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitf6815287adc097f2e9e30b4ea978d3c7::getLoader();

579
vendor/composer/ClassLoader.php vendored Normal file
View File

@ -0,0 +1,579 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
private $apcuPrefix;
/**
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

396
vendor/composer/InstalledVersions.php vendored Normal file
View File

@ -0,0 +1,396 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
* @internal
*/
private static $selfDir = null;
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool
*/
private static $installedIsLocalDir;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
// so we have to assume it does not, and that may result in duplicate data being returned when listing
// all installed packages for example
self::$installedIsLocalDir = false;
}
/**
* @return string
*/
private static function getSelfDir()
{
if (self::$selfDir === null) {
self::$selfDir = strtr(__DIR__, '\\', '/');
}
return self::$selfDir;
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
$copiedLocalDir = false;
if (self::$canGetVendors) {
$selfDir = self::getSelfDir();
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
$vendorDir = strtr($vendorDir, '\\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
self::$installedByVendor[$vendorDir] = $required;
$installed[] = $required;
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
self::$installed = $required;
self::$installedIsLocalDir = true;
}
}
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
$copiedLocalDir = true;
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
if (self::$installed !== array() && !$copiedLocalDir) {
$installed[] = self::$installed;
}
return $installed;
}
}

21
vendor/composer/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

10
vendor/composer/autoload_classmap.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);

10
vendor/composer/autoload_psr4.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'ParseCsv\\' => array($vendorDir . '/parsecsv/php-parsecsv/src'),
);

38
vendor/composer/autoload_real.php vendored Normal file
View File

@ -0,0 +1,38 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitf6815287adc097f2e9e30b4ea978d3c7
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitf6815287adc097f2e9e30b4ea978d3c7', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInitf6815287adc097f2e9e30b4ea978d3c7', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitf6815287adc097f2e9e30b4ea978d3c7::getInitializer($loader));
$loader->register(true);
return $loader;
}
}

36
vendor/composer/autoload_static.php vendored Normal file
View File

@ -0,0 +1,36 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitf6815287adc097f2e9e30b4ea978d3c7
{
public static $prefixLengthsPsr4 = array (
'P' =>
array (
'ParseCsv\\' => 9,
),
);
public static $prefixDirsPsr4 = array (
'ParseCsv\\' =>
array (
0 => __DIR__ . '/..' . '/parsecsv/php-parsecsv/src',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitf6815287adc097f2e9e30b4ea978d3c7::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitf6815287adc097f2e9e30b4ea978d3c7::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitf6815287adc097f2e9e30b4ea978d3c7::$classMap;
}, null, ClassLoader::class);
}
}

73
vendor/composer/installed.json vendored Normal file
View File

@ -0,0 +1,73 @@
{
"packages": [
{
"name": "parsecsv/php-parsecsv",
"version": "1.3.2",
"version_normalized": "1.3.2.0",
"source": {
"type": "git",
"url": "https://github.com/parsecsv/parsecsv-for-php.git",
"reference": "2d6236cae09133e0533d34ed45ba1e1ecafffebb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/parsecsv/parsecsv-for-php/zipball/2d6236cae09133e0533d34ed45ba1e1ecafffebb",
"reference": "2d6236cae09133e0533d34ed45ba1e1ecafffebb",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "^6",
"squizlabs/php_codesniffer": "^3.5"
},
"suggest": {
"illuminate/support": "Fluent array interface for map functions"
},
"time": "2021-11-07T14:15:46+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"ParseCsv\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Myhrberg",
"email": "contact@jimeh.me"
},
{
"name": "William Knauss",
"email": "will.knauss@gmail.com"
},
{
"name": "Susann Sgorzaly",
"homepage": "https://github.com/susgo"
},
{
"name": "Christian Bläul",
"homepage": "https://github.com/Fonata"
}
],
"description": "CSV data parser for PHP",
"support": {
"issues": "https://github.com/parsecsv/parsecsv-for-php/issues",
"source": "https://github.com/parsecsv/parsecsv-for-php"
},
"install-path": "../parsecsv/php-parsecsv"
}
],
"dev": true,
"dev-package-names": []
}

32
vendor/composer/installed.php vendored Normal file
View File

@ -0,0 +1,32 @@
<?php return array(
'root' => array(
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '2a30fbdcdbc8f6f2a59d2d0d87a5d6b9e8549fd6',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '2a30fbdcdbc8f6f2a59d2d0d87a5d6b9e8549fd6',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'parsecsv/php-parsecsv' => array(
'pretty_version' => '1.3.2',
'version' => '1.3.2.0',
'reference' => '2d6236cae09133e0533d34ed45ba1e1ecafffebb',
'type' => 'library',
'install_path' => __DIR__ . '/../parsecsv/php-parsecsv',
'aliases' => array(),
'dev_requirement' => false,
),
),
);

25
vendor/composer/platform_check.php vendored Normal file
View File

@ -0,0 +1,25 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50500)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.5.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
throw new \RuntimeException(
'Composer detected issues in your platform: ' . implode(' ', $issues)
);
}

View File

@ -0,0 +1,38 @@
---
name: CI
on:
push:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php_version:
- "7.4"
- "7.3"
- "7.2"
- "7.1"
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php_version }}
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer update
- name: Validate dependencies
run: composer validate
- name: Run tests
run: vendor/bin/phpunit --configuration tests/phpunit.xml

View File

@ -0,0 +1,21 @@
(The MIT license)
Copyright (c) 2014 Jim Myhrberg.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

246
vendor/parsecsv/php-parsecsv/README.md vendored Normal file
View File

@ -0,0 +1,246 @@
# ParseCsv
[![Financial Contributors on Open Collective](https://opencollective.com/parsecsv/all/badge.svg?label=financial+contributors)](https://opencollective.com/parsecsv)
ParseCsv is an easy-to-use PHP class that reads and writes CSV data properly. It
fully conforms to the specifications outlined on the on the
[Wikipedia article][CSV] (and thus RFC 4180). It has many advanced features which help make your
life easier when dealing with CSV data.
You may not need a library at all: before using ParseCsv, please make sure if PHP's own `str_getcsv()`, ``fgetcsv()`` or `fputcsv()` meets your needs.
This library was originally created in early 2007 by [jimeh](https://github.com/jimeh) due to the lack of built-in
and third-party support for handling CSV data in PHP.
[csv]: http://en.wikipedia.org/wiki/Comma-separated_values
## Features
* ParseCsv is a complete and fully featured CSV solution for PHP
* Supports enclosed values, enclosed commas, double quotes and new lines.
* Automatic delimiter character detection.
* Sort data by specific fields/columns.
* Easy data manipulation.
* Basic SQL-like _conditions_, _offset_ and _limit_ options for filtering
data.
* Error detection for incorrectly formatted input. It attempts to be
intelligent, but can not be trusted 100% due to the structure of CSV, and
how different programs like Excel for example outputs CSV data.
* Support for character encoding conversion using PHP's
`iconv()` and `mb_convert_encoding()` functions.
* Supports PHP 5.5 and higher.
It certainly works with PHP 7.2 and all versions in between.
## Installation
Installation is easy using Composer. Just run the following on the
command line:
```
composer require parsecsv/php-parsecsv
```
If you don't use a framework such as Drupal, Laravel, Symfony, Yii etc.,
you may have to manually include Composer's autoloader file in your PHP
script:
```php
require_once __DIR__ . '/vendor/autoload.php';
```
#### Without composer
Not recommended, but technically possible: you can also clone the
repository or extract the
[ZIP](https://github.com/parsecsv/parsecsv-for-php/archive/master.zip).
To use ParseCSV, you then have to add a `require 'parsecsv.lib.php';` line.
## Example Usage
**Parse a tab-delimited CSV file with encoding conversion**
```php
$csv = new \ParseCsv\Csv();
$csv->encoding('UTF-16', 'UTF-8');
$csv->delimiter = "\t";
$csv->parseFile('data.tsv');
print_r($csv->data);
```
**Auto-detect field delimiter character**
```php
$csv = new \ParseCsv\Csv();
$csv->auto('data.csv');
print_r($csv->data);
```
**Parse data with offset**
* ignoring the first X (e.g. two) rows
```php
$csv = new \ParseCsv\Csv();
$csv->offset = 2;
$csv->parseFile('data.csv');
print_r($csv->data);
```
**Limit the number of returned data rows**
```php
$csv = new \ParseCsv\Csv();
$csv->limit = 5;
$csv->parseFile('data.csv');
print_r($csv->data);
```
**Get total number of data rows without parsing whole data**
* Excluding heading line if present (see $csv->header property)
```php
$csv = new \ParseCsv\Csv();
$csv->loadFile('data.csv');
$count = $csv->getTotalDataRowCount();
print_r($count);
```
**Get most common data type for each column**
```php
$csv = new \ParseCsv\Csv('data.csv');
$csv->getDatatypes();
print_r($csv->data_types);
```
**Modify data in a CSV file**
Change data values:
```php
$csv = new \ParseCsv\Csv();
$csv->sort_by = 'id';
$csv->parseFile('data.csv');
# "4" is the value of the "id" column of the CSV row
$csv->data[4] = array('firstname' => 'John', 'lastname' => 'Doe', 'email' => 'john@doe.com');
$csv->save();
```
Enclose each data value by quotes:
```php
$csv = new \ParseCsv\Csv();
$csv->parseFile('data.csv');
$csv->enclose_all = true;
$csv->save();
```
**Replace field names or set ones if missing**
```php
$csv = new \ParseCsv\Csv();
$csv->fields = ['id', 'name', 'category'];
$csv->parseFile('data.csv');
```
**Add row/entry to end of CSV file**
_Only recommended when you know the exact structure of the file._
```php
$csv = new \ParseCsv\Csv();
$csv->save('data.csv', array(array('1986', 'Home', 'Nowhere', '')), /* append */ true);
```
**Convert 2D array to CSV data and send headers to browser to treat output as
a file and download it**
Your web app users would call this an export.
```php
$csv = new \ParseCsv\Csv();
$csv->linefeed = "\n";
$header = array('field 1', 'field 2');
$csv->output('movies.csv', $data_array, $header, ',');
```
For more complex examples, see the ``tests`` and `examples` directories.
## Test coverage
All tests are located in the `tests` directory. To execute tests, run the following commands:
````bash
composer install
composer run test
````
When pushing code to GitHub, tests will be executed using GitHub Actions. The relevant configuration is in the
file `.github/workflows/ci.yml`. To run the `test` action locally, you can execute the following command:
````bash
make local-ci
````
## Security
If you discover any security related issues, please email ParseCsv@blaeul.de instead of using GitHub issues.
## Credits
* ParseCsv is based on the concept of [Ming Hong Ng][ming]'s [CsvFileParser][]
class.
[ming]: http://minghong.blogspot.com/
[CsvFileParser]: http://minghong.blogspot.com/2006/07/csv-parser-for-php.html
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute.
Please find a complete list on the project's [contributors][] page.
[contributors]: https://github.com/parsecsv/parsecsv-for-php/graphs/contributors
<a href="https://github.com/parsecsv/parsecsv-for-php/graphs/contributors"><img src="https://opencollective.com/parsecsv/contributors.svg?width=890&button=false" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/parsecsv/contribute)]
#### Individuals
<a href="https://opencollective.com/parsecsv"><img src="https://opencollective.com/parsecsv/individuals.svg?width=890"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/parsecsv/contribute)]
<a href="https://opencollective.com/parsecsv/organization/0/website"><img src="https://opencollective.com/parsecsv/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/1/website"><img src="https://opencollective.com/parsecsv/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/2/website"><img src="https://opencollective.com/parsecsv/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/3/website"><img src="https://opencollective.com/parsecsv/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/4/website"><img src="https://opencollective.com/parsecsv/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/5/website"><img src="https://opencollective.com/parsecsv/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/6/website"><img src="https://opencollective.com/parsecsv/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/7/website"><img src="https://opencollective.com/parsecsv/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/8/website"><img src="https://opencollective.com/parsecsv/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/9/website"><img src="https://opencollective.com/parsecsv/organization/9/avatar.svg"></a>
## License
(The MIT license)
Copyright (c) 2014 Jim Myhrberg.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[![Build Status](https://travis-ci.org/parsecsv/parsecsv-for-php.svg?branch=master)](https://travis-ci.org/parsecsv/parsecsv-for-php)

View File

@ -0,0 +1,57 @@
{
"name": "parsecsv/php-parsecsv",
"description": "CSV data parser for PHP",
"license": "MIT",
"authors": [
{
"name": "Jim Myhrberg",
"email": "contact@jimeh.me"
},
{
"name": "William Knauss",
"email": "will.knauss@gmail.com"
},
{
"name": "Susann Sgorzaly",
"homepage": "https://github.com/susgo"
},
{
"name": "Christian Bläul",
"homepage": "https://github.com/Fonata"
}
],
"autoload": {
"psr-4": {
"ParseCsv\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"ParseCsv\\tests\\": "tests"
}
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "^6",
"squizlabs/php_codesniffer": "^3.5"
},
"suggest": {
"illuminate/support": "Fluent array interface for map functions"
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"scripts": {
"test": [
"vendor/bin/phpunit -c tests tests --disallow-test-output --coverage-clover coverage_clover.xml --whitelist src"
]
},
"support": {
"issues": "https://github.com/parsecsv/parsecsv-for-php/issues",
"source": "https://github.com/parsecsv/parsecsv-for-php"
}
}

View File

@ -0,0 +1,24 @@
<?php
// This file should not be used at all! Instead, please use Composer's autoload.
// It purely exists to reduce the maintenance burden for existing code using
// this repository.
// Check if people used Composer to include this project in theirs
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
require __DIR__ . '/src/enums/AbstractEnum.php';
require __DIR__ . '/src/enums/DatatypeEnum.php';
require __DIR__ . '/src/enums/FileProcessingModeEnum.php';
require __DIR__ . '/src/enums/SortEnum.php';
require __DIR__ . '/src/extensions/DatatypeTrait.php';
require __DIR__ . '/src/Csv.php';
} else {
require __DIR__ . '/vendor/autoload.php';
}
// This wrapper class should not be used by new projects. Please look at the
// examples to find the up-to-date way of using this repo.
class parseCSV extends ParseCsv\Csv {
}

1472
vendor/parsecsv/php-parsecsv/src/Csv.php vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
<?php
namespace ParseCsv\enums;
abstract class AbstractEnum {
/**
* Creates a new value of some type
*
* @param mixed $value
*
* @throws \UnexpectedValueException if incompatible type is given.
*/
public function __construct($value) {
if (!$this->isValid($value)) {
throw new \UnexpectedValueException("Value '$value' is not part of the enum " . get_called_class());
}
$this->value = $value;
}
public static function getConstants() {
$class = get_called_class();
$reflection = new \ReflectionClass($class);
return $reflection->getConstants();
}
/**
* Check if enum value is valid
*
* @param $value
*
* @return bool
*/
public static function isValid($value) {
return in_array($value, static::getConstants(), true);
}
}

View File

@ -0,0 +1,120 @@
<?php
namespace ParseCsv\enums;
/**
* Class DatatypeEnum
*
* @package ParseCsv\enums
*
* todo: needs a basic parent enum class for error handling.
*/
class DatatypeEnum extends AbstractEnum {
const __DEFAULT = self::TYPE_STRING;
const TYPE_STRING = 'string';
const TYPE_FLOAT = 'float';
const TYPE_INT = 'integer';
const TYPE_BOOL = 'boolean';
const TYPE_DATE = 'date';
const REGEX_FLOAT = '/(^[+-]?$)|(^[+-]?[0-9]+([,.][0-9])?[0-9]*(e[+-]?[0-9]+)?$)/';
const REGEX_INT = '/^[-+]?[0-9]\d*$/';
const REGEX_BOOL = '/^(?i:true|false)$/';
/**
* Define validator functions here.
*
* @var array
*
* @uses isValidFloat
* @uses isValidInteger
* @uses isValidBoolean
* @uses isValidDate
*/
private static $validators = array(
self::TYPE_STRING => null,
self::TYPE_INT => 'isValidInteger',
self::TYPE_BOOL => 'isValidBoolean',
self::TYPE_FLOAT => 'isValidFloat',
self::TYPE_DATE => 'isValidDate',
);
/**
* Checks data type for given string.
*
* @param string $value
*
* @return bool|string
*/
public static function getValidTypeFromSample($value) {
$value = trim((string) $value);
if (empty($value)) {
return false;
}
foreach (self::$validators as $type => $validator) {
if ($validator === null) {
continue;
}
if (method_exists(__CLASS__, $validator) && self::$validator($value)) {
return $type;
}
}
return self::__DEFAULT;
}
/**
* Check if string is float value.
*
* @param string $value
*
* @return bool
*/
private static function isValidFloat($value) {
return (bool) preg_match(self::REGEX_FLOAT, $value);
}
/**
* Check if string is integer value.
*
* @param string $value
*
* @return bool
*/
private static function isValidInteger($value) {
return (bool) preg_match(self::REGEX_INT, $value);
}
/**
* Check if string is boolean.
*
* @param string $value
*
* @return bool
*/
private static function isValidBoolean($value) {
return (bool) preg_match(self::REGEX_BOOL, $value);
}
/**
* Check if string is date.
*
* @param string $value
*
* @return bool
*/
private static function isValidDate($value) {
return (bool) strtotime($value);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace ParseCsv\enums;
/**
* Class FileProcessingEnum
*
* @package ParseCsv\enums
*
* todo extends a basic enum class after merging #121
*/
class FileProcessingModeEnum {
const __default = self::MODE_FILE_OVERWRITE;
const MODE_FILE_APPEND = true;
const MODE_FILE_OVERWRITE = false;
public static function getAppendMode($mode) {
if ($mode == self::MODE_FILE_APPEND) {
return 'ab';
}
return 'wb';
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace ParseCsv\enums;
class SortEnum extends AbstractEnum {
const __DEFAULT = self::SORT_TYPE_REGULAR;
const SORT_TYPE_REGULAR = 'regular';
const SORT_TYPE_NUMERIC = 'numeric';
const SORT_TYPE_STRING = 'string';
private static $sorting = array(
self::SORT_TYPE_REGULAR => SORT_REGULAR,
self::SORT_TYPE_STRING => SORT_STRING,
self::SORT_TYPE_NUMERIC => SORT_NUMERIC,
);
public static function getSorting($type) {
if (array_key_exists($type, self::$sorting)) {
return self::$sorting[$type];
}
return self::$sorting[self::__DEFAULT];
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace ParseCsv\extensions;
use ParseCsv\enums\DatatypeEnum;
trait DatatypeTrait {
/**
* Data Types
* Data types of CSV data-columns, keyed by the column name. Possible values
* are string, float, integer, boolean, date. See DatatypeEnum.
*
* @var array
*/
public $data_types = [];
/**
* Check data type for one column.
* Check for most commonly data type for one column.
*
* @param array $datatypes
*
* @return string|false
*/
private function getMostFrequentDatatypeForColumn($datatypes) {
// remove 'false' from array (can happen if CSV cell is empty)
$typesFiltered = array_filter($datatypes);
if (empty($typesFiltered)) {
return false;
}
$typesFreq = array_count_values($typesFiltered);
arsort($typesFreq);
reset($typesFreq);
return key($typesFreq);
}
/**
* Check data type foreach Column
* Check data type for each column and returns the most commonly.
*
* Requires PHP >= 5.5
*
* @uses DatatypeEnum::getValidTypeFromSample
*
* @return array|bool
*/
public function getDatatypes() {
if (empty($this->data)) {
$this->data = $this->_parse_string();
}
if (!is_array($this->data)) {
throw new \UnexpectedValueException('No data set yet.');
}
$result = [];
foreach ($this->titles as $cName) {
$column = array_column($this->data, $cName);
$cDatatypes = array_map(DatatypeEnum::class . '::getValidTypeFromSample', $column);
$result[$cName] = $this->getMostFrequentDatatypeForColumn($cDatatypes);
}
$this->data_types = $result;
return !empty($this->data_types) ? $this->data_types : [];
}
/**
* Check data type of titles / first row for auto detecting if this could be
* a heading line.
*
* Requires PHP >= 5.5
*
* @uses DatatypeEnum::getValidTypeFromSample
*
* @return bool
*/
public function autoDetectFileHasHeading() {
if (empty($this->data)) {
throw new \UnexpectedValueException('No data set yet.');
}
if ($this->heading) {
$firstRow = $this->titles;
} else {
$firstRow = $this->data[0];
}
$firstRow = array_filter($firstRow);
if (empty($firstRow)) {
return false;
}
$firstRowDatatype = array_map(DatatypeEnum::class . '::getValidTypeFromSample', $firstRow);
return $this->getMostFrequentDatatypeForColumn($firstRowDatatype) === DatatypeEnum::TYPE_STRING;
}
}