Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d0660a262 |
145
admin_dashboard.php
Normal file
145
admin_dashboard.php
Normal 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
39
assets/css/style.css
Normal 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
150
blog.php
Normal 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 →</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 →</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 →</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="#">← Older</a>
|
||||||
|
</li>
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<a class="page-link" href="#">Newer →</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
5
composer.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"parsecsv/php-parsecsv": "^1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
83
composer.lock
generated
Normal file
83
composer.lock
generated
Normal 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
BIN
composer.phar
Executable file
Binary file not shown.
18
cron/send_reminders.php
Normal file
18
cron/send_reminders.php
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
51
db/migrations/001_create_initial_tables.sql
Normal file
51
db/migrations/001_create_initial_tables.sql
Normal 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;
|
||||||
|
|
||||||
8
db/migrations/002_create_password_resets_table.sql
Normal file
8
db/migrations/002_create_password_resets_table.sql
Normal 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;
|
||||||
13
db/migrations/003_create_leave_requests_table.sql
Normal file
13
db/migrations/003_create_leave_requests_table.sql
Normal 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
41
export_report.php
Normal 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
75
forgot_password.php
Normal 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
43
import_students.php
Normal 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
10
includes/footer.php
Normal 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
10
includes/header.php
Normal 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>
|
||||||
225
index.php
225
index.php
@ -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'] ?? '';
|
|
||||||
?>
|
|
||||||
<?php if ($projectDescription): ?>
|
|
||||||
<!-- Meta description -->
|
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
|
||||||
<!-- Open Graph meta tags -->
|
|
||||||
<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>
|
<?php endif; ?>
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
<div class="form-floating mb-3">
|
||||||
|
<input type="email" class="form-control" id="floatingInput" name="email" placeholder="name@example.com" required>
|
||||||
|
<label for="floatingInput">Email address</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-floating mb-3">
|
||||||
|
<input type="password" class="form-control" id="floatingPassword" name="password" placeholder="Password" required>
|
||||||
|
<label for="floatingPassword">Password</label>
|
||||||
|
</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">© <?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
5
logout.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
session_destroy();
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
101
reset_password.php
Normal file
101
reset_password.php
Normal 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
103
student_dashboard.php
Normal 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
52
submit_leave_request.php
Normal 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
75
teacher_dashboard.php
Normal 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
85
update_leave_status.php
Normal 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
22
vendor/autoload.php
vendored
Normal 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
579
vendor/composer/ClassLoader.php
vendored
Normal 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
396
vendor/composer/InstalledVersions.php
vendored
Normal 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
21
vendor/composer/LICENSE
vendored
Normal 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
10
vendor/composer/autoload_classmap.php
vendored
Normal 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',
|
||||||
|
);
|
||||||
9
vendor/composer/autoload_namespaces.php
vendored
Normal file
9
vendor/composer/autoload_namespaces.php
vendored
Normal 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
10
vendor/composer/autoload_psr4.php
vendored
Normal 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
38
vendor/composer/autoload_real.php
vendored
Normal 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
36
vendor/composer/autoload_static.php
vendored
Normal 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
73
vendor/composer/installed.json
vendored
Normal 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
32
vendor/composer/installed.php
vendored
Normal 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
25
vendor/composer/platform_check.php
vendored
Normal 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)
|
||||||
|
);
|
||||||
|
}
|
||||||
38
vendor/parsecsv/php-parsecsv/.github/workflows/ci.yml
vendored
Normal file
38
vendor/parsecsv/php-parsecsv/.github/workflows/ci.yml
vendored
Normal 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
|
||||||
21
vendor/parsecsv/php-parsecsv/License.txt
vendored
Normal file
21
vendor/parsecsv/php-parsecsv/License.txt
vendored
Normal 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
246
vendor/parsecsv/php-parsecsv/README.md
vendored
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
# ParseCsv
|
||||||
|
[](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.
|
||||||
|
|
||||||
|
[](https://travis-ci.org/parsecsv/parsecsv-for-php)
|
||||||
57
vendor/parsecsv/php-parsecsv/composer.json
vendored
Normal file
57
vendor/parsecsv/php-parsecsv/composer.json
vendored
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
24
vendor/parsecsv/php-parsecsv/parsecsv.lib.php
vendored
Normal file
24
vendor/parsecsv/php-parsecsv/parsecsv.lib.php
vendored
Normal 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
1472
vendor/parsecsv/php-parsecsv/src/Csv.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
38
vendor/parsecsv/php-parsecsv/src/enums/AbstractEnum.php
vendored
Normal file
38
vendor/parsecsv/php-parsecsv/src/enums/AbstractEnum.php
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
120
vendor/parsecsv/php-parsecsv/src/enums/DatatypeEnum.php
vendored
Normal file
120
vendor/parsecsv/php-parsecsv/src/enums/DatatypeEnum.php
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
vendor/parsecsv/php-parsecsv/src/enums/FileProcessingModeEnum.php
vendored
Normal file
28
vendor/parsecsv/php-parsecsv/src/enums/FileProcessingModeEnum.php
vendored
Normal 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';
|
||||||
|
}
|
||||||
|
}
|
||||||
29
vendor/parsecsv/php-parsecsv/src/enums/SortEnum.php
vendored
Normal file
29
vendor/parsecsv/php-parsecsv/src/enums/SortEnum.php
vendored
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
102
vendor/parsecsv/php-parsecsv/src/extensions/DatatypeTrait.php
vendored
Normal file
102
vendor/parsecsv/php-parsecsv/src/extensions/DatatypeTrait.php
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user