>
+ */
+ public array $decorators = [];
+}
diff --git a/app/Controllers/ApiController.php b/app/Controllers/ApiController.php
new file mode 100644
index 0000000..c8d61ff
--- /dev/null
+++ b/app/Controllers/ApiController.php
@@ -0,0 +1,310 @@
+request->getServer('PHP_AUTH_USER');
+ $password = $this->request->getServer('PHP_AUTH_PW');
+
+ // ------------------------------------------------------------------------------------------
+ // Buat Objek untuk Auth Controller
+ // ------------------------------------------------------------------------------------------
+ $securityController = new SecurityController();
+ $auth = $securityController->auth_check($username, $password);
+ // $auth = True;
+
+ /////////////////////////////////////////////////////////////////////////////////////////////
+ // 1 - Untuk Autentikasi dan Lain"
+ /////////////////////////////////////////////////////////////////////////////////////////////
+ // ------------------------------------------------------------------------------------------
+ // Kredensial tidak valid, kembalikan pesan kesalahan dan Berhenti
+ // ------------------------------------------------------------------------------------------
+ if ( $auth == False) {
+ return $this->respond(["message" => "Maaf Akses Anda Ditolak"], 401);
+ }
+ // ------------------------------------------------------------------------------------------
+ // Terima data JSON dan Masukkan ke Variabel
+ // ------------------------------------------------------------------------------------------
+ $instrument_data_with_checksum = $this->request->getJSON();
+ $encrypted_instrument_data = $instrument_data_with_checksum->instrument_data;
+ $checksum_data = $instrument_data_with_checksum->checksum;
+ // ------------------------------------------------------------------------------------------
+ // Dekripsi Data JSON, Jika Tidak cocok kembalikan pesan kesalahan dan Berhenti
+ // ------------------------------------------------------------------------------------------
+ $decrypted_data = $securityController->decryptData($encrypted_instrument_data);
+ if ($decrypted_data == False) {
+ return $this->respond(["message" => "Enkripsi Tidak Cocok "], 400);
+ }
+ // ------------------------------------------------------------------------------------------
+ // Checksum Cek, Jika Tidak cocok kembalikan pesan kesalahan dan Berhenti
+ // ------------------------------------------------------------------------------------------
+ if ( $securityController->checksum_check($decrypted_data, $checksum_data) == False) {
+ return $this->respond(["message" => "Checksum Tidak Cocok "], 400);
+ }
+
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////////
+ // 2 - Olah Data Untuk Disimpan ke Database
+ /////////////////////////////////////////////////////////////////////////////////////////////
+ // ------------------------------------------------------------------------------------------
+ // Decode Untuk Merubah Format Data Menjadi Array Asosiatif
+ // ------------------------------------------------------------------------------------------
+ $db = \Config\Database::connect();
+ $decrypted_data = json_decode($decrypted_data, true);
+
+ $total_simpan_non = count($decrypted_data['result_data']);
+ $total_simpan_non = strval($total_simpan_non);
+
+ // Digunakna untuk memfilter data
+ $decrypted_data = $this->filterData($decrypted_data);
+ $sn_number = $decrypted_data['sn_number'];
+
+ $total_simpan_filter = count($decrypted_data['result_data']);
+ $total_simpan_filter = strval($total_simpan_filter);
+
+ // Mulai transaksi
+ $db->transStart();
+
+ // Menyiapkan array untuk menyimpan data batch
+ $patresTechValues = [];
+ $patresFlagValues = [];
+ $flagdefCache = [];
+
+ // Proses setiap item dalam data yang diterima
+ foreach ($decrypted_data['result_data'] as $key => $value) {
+ $SAMP_ID = $value['SAMP_ID'];
+ $ITEM_NAME = $value['ITEM_NAME'];
+ $ASP_CNT = $value['ASP_CNT'];
+
+ // $CONC_DATA = isset($value['CONC_DATA']) ? $value['CONC_DATA'] : null;
+ // $OD_DATA = isset($value['OD_DATA']) ? $value['OD_DATA'] : null;
+
+ // Jika tidak ada nilai, set sebagai NULL
+ $CONC_DATA = isset($value['CONC_DATA']) ? "'".$value['CONC_DATA']."'" : 'NULL';
+ $OD_DATA = isset($value['OD_DATA']) ? "'".$value['OD_DATA']."'" : 'NULL';
+
+ $RST_DATE = $value['RST_DATE'];
+ $FLAG = $value['Flag'];
+ $REACTION_NO = $value['ReactionNo'];
+ $DIL_ORD = $value['DIL_ORD'];
+
+ // Input ke Tabel PATRES - Input Satu Persatu
+ $sql = "INSERT INTO patres (EquipmentID, SampleID, TestTechCode, Aspcnt, Result, ResultDateTime, createdate)
+ VALUES ('$sn_number', '$SAMP_ID', '$ITEM_NAME', $ASP_CNT, $CONC_DATA, '$RST_DATE', NOW())";
+ $db->query($sql);
+
+ $patres_lastid = $db->insertID();
+
+ // Untuk Inputan ke Tabel PATRESTECH - Batch Input
+ $patresTechValues[] = " ($patres_lastid, 'OD_DATA', $OD_DATA, '$RST_DATE'),
+ ($patres_lastid, 'REACTION_NO', '$REACTION_NO', '$RST_DATE'),
+ ($patres_lastid, 'DIL_ORD', '$DIL_ORD', '$RST_DATE')";
+
+ // Cek apakah FLAG sudah ada di cache
+ if ($FLAG !== null) {
+ if (!isset($flagdefCache[$FLAG])) {
+ // Cek tabel flagdef untuk FLAG
+ $sql = "SELECT FlagDefID as flagdef_id FROM flagdef WHERE flag='$FLAG'";
+ $query = $db->query($sql);
+ $row = $query->getRow();
+
+ if ($row !== null) {
+ $FlagDefID = (int) $row->flagdef_id;
+ } else {
+ // Insert Data ke Tabel Flagdef
+ $sql = "INSERT INTO flagdef (Instrumentid, Flag, flagtext, FlagDesc, onscreen, onresult, createdate)
+ VALUES (1, '$FLAG', '$FLAG', '', 1, 1, NOW())";
+ $db->query($sql);
+
+ // Ambil FlagDefID baru
+ $sql = "SELECT FlagDefID as flagdef_id FROM flagdef WHERE flag='$FLAG'";
+ $query = $db->query($sql);
+ $row = $query->getRow();
+ $FlagDefID = (int) $row->flagdef_id;
+ }
+
+ // Simpan FlagDefID di cache
+ $flagdefCache[$FLAG] = $FlagDefID;
+
+ } else {
+ $FlagDefID = $flagdefCache[$FLAG];
+ }
+
+ // Siapkan query untuk INSERT INTO patresflag
+ $patresFlagValues[] = "($patres_lastid, $FlagDefID, '$RST_DATE')";
+ }
+ }
+
+ // Menyisipkan data ke tabel patresflag dalam satu batch
+ if (!empty($patresFlagValues)) {
+ $sql = "INSERT INTO patresflag (resultid, flagid, createdate)
+ VALUES " . implode(", ", $patresFlagValues);
+ $db->query($sql);
+ }
+
+ // Menyisipkan data ke tabel patrestech dalam satu batch
+ if (!empty($patresTechValues)) {
+ $sql = "INSERT INTO patrestech (resultid, DBField, DBValue, createdate)
+ VALUES " . implode(", ", $patresTechValues);
+ $db->query($sql);
+ }
+
+ $db->transComplete();
+
+ // Cek status transaksi
+ if ($db->transStatus() === FALSE) {
+ $db->transRollback();
+ // return $this->respond(['message' => 'Server tidak menyimpan data anda, dikarenakan terjadi kesalahan saat memproses data.'], 500);
+ return $this->respond(['message' => $db->error()], 500);
+ } else {
+ $db->transCommit();
+ return $this->respond(['message' => "Menyimpan ". $total_simpan_filter . " dari " . $total_simpan_non ." data, Data berhasil diproses."], 201);
+ }
+
+ }
+
+ // Digunakan Untuk Memfilter Data Valid dan Tidak Valid
+ // Misal ada 2 Data :
+ // dengan Sampid dan semuanya sama namun beda flag maka cukup pilih/simpan data yang punya flag
+ public function filterData($decrypted_data) {
+
+ $filtered_data = array();
+
+ $filtered_data['sn_number'] = $decrypted_data['sn_number'];
+ $filtered_data['result_data'] = [];
+
+ $i = 0;
+ $status = false;
+ $length_array = count($decrypted_data['result_data']);
+
+ foreach ($decrypted_data['result_data'] as $key => $value) {
+
+ $SAMP_ID = $value['SAMP_ID'];
+ $ITEM_NAME = $value['ITEM_NAME'];
+ $ASP_CNT = $value['ASP_CNT'];
+ $CONC_DATA = $value['CONC_DATA'];
+ $OD_DATA = $value['OD_DATA'];
+ $RST_DATE = $value['RST_DATE'];
+ $FLAG = $value['Flag'];
+ $REACTION_NO = $value['ReactionNo'];
+ $DIL_ORD = $value['DIL_ORD'];
+
+ // Kondisi saat array bernilai 1
+ if ($i != 0) {
+
+ // Kondisi jika ada data array yang sama antara array 0 dan 1, 1 dan 2, dan seterusnya
+ if ( $SAMP_ID == $decrypted_data['result_data'][($i-1)]['SAMP_ID'] AND
+ $ITEM_NAME == $decrypted_data['result_data'][($i-1)]['ITEM_NAME'] AND
+ $ASP_CNT == $decrypted_data['result_data'][($i-1)]['ASP_CNT'] AND
+ $CONC_DATA == $decrypted_data['result_data'][($i-1)]['CONC_DATA'] AND
+ $OD_DATA == $decrypted_data['result_data'][($i-1)]['OD_DATA'] AND
+ $RST_DATE == $decrypted_data['result_data'][($i-1)]['RST_DATE'] AND
+ $REACTION_NO == $decrypted_data['result_data'][($i-1)]['ReactionNo']
+ ) {
+
+ // Record Sebelumnya
+ $prev = [
+ "SAMP_ID" => $decrypted_data['result_data'][($i-1)]['SAMP_ID'],
+ "ITEM_NAME" => $decrypted_data['result_data'][($i-1)]['ITEM_NAME'],
+ "ASP_CNT" => $decrypted_data['result_data'][($i-1)]['ASP_CNT'],
+ "CONC_DATA" => $decrypted_data['result_data'][($i-1)]['CONC_DATA'],
+ "OD_DATA" => $decrypted_data['result_data'][($i-1)]['OD_DATA'],
+ "RST_DATE" => $decrypted_data['result_data'][($i-1)]['RST_DATE'],
+ "Flag" => $decrypted_data['result_data'][($i-1)]['Flag'],
+ "ReactionNo" => $decrypted_data['result_data'][($i-1)]['ReactionNo'],
+ "DIL_ORD" => $decrypted_data['result_data'][($i-1)]['DIL_ORD'],
+ ];
+
+ // Record Saat ini
+ $current = [
+ "SAMP_ID" => $decrypted_data['result_data'][($i)]['SAMP_ID'],
+ "ITEM_NAME" => $decrypted_data['result_data'][($i)]['ITEM_NAME'],
+ "ASP_CNT" => $decrypted_data['result_data'][($i)]['ASP_CNT'],
+ "CONC_DATA" => $decrypted_data['result_data'][($i)]['CONC_DATA'],
+ "OD_DATA" => $decrypted_data['result_data'][($i)]['OD_DATA'],
+ "RST_DATE" => $decrypted_data['result_data'][($i)]['RST_DATE'],
+ "Flag" => $decrypted_data['result_data'][($i)]['Flag'],
+ "ReactionNo" => $decrypted_data['result_data'][($i)]['ReactionNo'],
+ "DIL_ORD" => $decrypted_data['result_data'][($i)]['DIL_ORD'],
+ ];
+
+ // Simpan yang memiliki nilai flag ke temp data
+ if ($current['Flag'] == null) {
+ array_push($filtered_data['result_data'], $prev);
+ } else if ($prev['Flag'] == null) {
+ array_push($filtered_data['result_data'], $current);
+ }
+
+ // Status untuk melewati/skip index array saat ini
+ $status = true;
+
+ // Kondisi saat record tidak sama
+ } else {
+
+ // Jika sudah menyimpan record ganda diatas maka jangan jalankan ini
+ if ($status == false) {
+
+ $current = [
+ "SAMP_ID" => $decrypted_data['result_data'][($i-1)]['SAMP_ID'],
+ "ITEM_NAME" => $decrypted_data['result_data'][($i-1)]['ITEM_NAME'],
+ "ASP_CNT" => $decrypted_data['result_data'][($i-1)]['ASP_CNT'],
+ "CONC_DATA" => $decrypted_data['result_data'][($i-1)]['CONC_DATA'],
+ "OD_DATA" => $decrypted_data['result_data'][($i-1)]['OD_DATA'],
+ "RST_DATE" => $decrypted_data['result_data'][($i-1)]['RST_DATE'],
+ "Flag" => $decrypted_data['result_data'][($i-1)]['Flag'],
+ "ReactionNo" => $decrypted_data['result_data'][($i-1)]['ReactionNo'],
+ "DIL_ORD" => $decrypted_data['result_data'][($i-1)]['DIL_ORD'],
+ ];
+
+ array_push($filtered_data['result_data'], $current);
+
+ // Ubah status
+ } else {
+ $status = false;
+ }
+
+ // Digunakan untuk menyimpan record terakhir
+ if ($i == ($length_array-1)) {
+
+ $current = [
+ "SAMP_ID" => $decrypted_data['result_data'][($i)]['SAMP_ID'],
+ "ITEM_NAME" => $decrypted_data['result_data'][($i)]['ITEM_NAME'],
+ "ASP_CNT" => $decrypted_data['result_data'][($i)]['ASP_CNT'],
+ "CONC_DATA" => $decrypted_data['result_data'][($i)]['CONC_DATA'],
+ "OD_DATA" => $decrypted_data['result_data'][($i)]['OD_DATA'],
+ "RST_DATE" => $decrypted_data['result_data'][($i)]['RST_DATE'],
+ "Flag" => $decrypted_data['result_data'][($i)]['Flag'],
+ "ReactionNo" => $decrypted_data['result_data'][($i)]['ReactionNo'],
+ "DIL_ORD" => $decrypted_data['result_data'][($i)]['DIL_ORD'],
+ ];
+
+ array_push($filtered_data['result_data'], $current);
+ }
+
+ }
+
+ }
+
+ $i++;
+ }
+
+ return $filtered_data;
+ }
+
+}
diff --git a/app/Controllers/ApiServiceController.php b/app/Controllers/ApiServiceController.php
new file mode 100644
index 0000000..3acd86e
--- /dev/null
+++ b/app/Controllers/ApiServiceController.php
@@ -0,0 +1,79 @@
+query($sql);
+ // $data = $query->getResultArray();
+ // if(count($data)==0) { $data = array('status' => 'error', 'message' => 'No data found'); }
+ // else {
+ // header('Content-Type: application/json');
+ // echo json_encode($data);
+ // }
+ // }
+
+ // Get Data untuk data Test
+ public function getPatresCount(...$segments) {
+
+ $resultsData = array();
+
+ $db = \Config\Database::connect();
+
+ // Mulai Transaksi
+ $db->transStart();
+
+ foreach ($segments as $value) {
+
+ if ($value == '6011310722' OR $value == '6015090124'
+ OR $value == '6011320722' OR $value == '6005840519'
+ OR $value == '6006100619' OR $value == '6015560324') {
+
+ $sql = "
+ SELECT
+ COUNT(DISTINCT p.ResultID) AS total_tests
+ FROM
+ patres p
+ JOIN
+ patrestech pt ON p.ResultID = pt.ResultID
+ WHERE
+ pt.DBField = 'REACTION_NO'
+ AND EquipmentID = ?
+ AND pt.DBValue != '0'
+ AND p.ResultDateTime >= DATE_SUB(NOW(), INTERVAL 6 MONTH);
+ ";
+
+ $query = $db->query($sql, [$value]);
+ $data = $query->getResultArray();
+ $result = $data[0]['total_tests'] ?? null;
+
+ if ($result !== null) {
+ $resultsData[$value] = $result;
+ }
+
+ } else {
+ continue;
+ }
+
+
+ }
+
+ // Commit Transaksi
+ $db->transComplete();
+
+ // Set the header to JSON and return the data
+ header('Content-Type: application/json');
+
+ if (count($resultsData) == 0) {
+ echo json_encode(['status' => 'error', 'message' => 'No data found']);
+ } else {
+ return $this->response->setJSON($resultsData);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/Controllers/BaseController copy.php b/app/Controllers/BaseController copy.php
new file mode 100644
index 0000000..fb44007
--- /dev/null
+++ b/app/Controllers/BaseController copy.php
@@ -0,0 +1,58 @@
+session = \Config\Services::session();
+ }
+}
diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php
new file mode 100644
index 0000000..689405b
--- /dev/null
+++ b/app/Controllers/BaseController.php
@@ -0,0 +1,58 @@
+
+ */
+ protected $helpers = [];
+
+ /**
+ * Be sure to declare properties for any property fetch you initialized.
+ * The creation of dynamic property is deprecated in PHP 8.2.
+ */
+ // protected $session;
+
+ /**
+ * @return void
+ */
+ public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
+ {
+ // Do Not Edit This Line
+ parent::initController($request, $response, $logger);
+
+ // Preload any models, libraries, etc, here.
+
+ // E.g.: $this->session = service('session');
+ }
+}
diff --git a/app/Controllers/Commands.php b/app/Controllers/Commands.php
new file mode 100644
index 0000000..fe318ef
--- /dev/null
+++ b/app/Controllers/Commands.php
@@ -0,0 +1,106 @@
+query($sql);
+ // $results = $query->getResultArray();
+ // $data['flagdef'] = $results;
+
+ return view('broadcast_command_index.php');
+ }
+
+ public function indexSpesific() {
+ // $db = \Config\Database::connect();
+ // $sql = "SELECT * FROM flagdef";
+ // $query = $db->query($sql);
+ // $results = $query->getResultArray();
+ // $data['flagdef'] = $results;
+
+ return view('spesific_command_index.php');
+ }
+
+ // public function view($FlagDefID) {
+ // $FlagDefID = (int) $FlagDefID;
+ // $db = \Config\Database::connect();
+ // $sql = "SELECT * FROM flagdef WHERE FlagDefID=$FlagDefID";
+ // $query = $db->query($sql);
+ // $results = $query->getResultArray();
+ // $data['flagdef'] = $results;
+
+ // return view('flags_view.php', $data);
+ // }
+
+ // public function edit($FlagDefID) {
+ // $data = array();
+ // $data['instsAlias'] = $this->getProductAlias();
+
+ // $db = \Config\Database::connect();
+
+ // if ($FlagDefID != 0) {
+ // $sql = "SELECT * FROM flagdef where FlagDefID='$FlagDefID'";
+ // $query = $db->query($sql);
+ // $results = $query->getResultArray();
+ // $data['flagdef'] = $results;
+ // }
+
+ // if ($this->request->getMethod() === 'post') {
+
+ // $rules = [ 'flagtext' => 'required'];
+
+ // $flagdefid = $this->request->getPost('flagdefid');
+ // $instrumentid = $this->request->getPost('instrumentid');
+ // $flag = $this->request->getPost('flag');
+ // $flagtext = $this->request->getPost('flagtext');
+ // $flagdesc = $this->request->getPost('flagdesc');
+ // $onscreen = $this->request->getPost('onscreen');
+ // $onresult = $this->request->getPost('onresult');
+
+ // if ($this->validate($rules)){
+
+ // if ($flagdefid == 0 ) {
+ // $sql = "INSERT INTO `flagdef` ( `InstrumentID`, `Flag`, `FlagText`, `FlagDesc`, `OnScreen`, `OnResult`, `CreateDate` )
+ // VALUES ( $instrumentid, '$flag', '$flagtext', '$flagdesc', $onscreen, $onresult ,NOW())";
+ // $query = $db->query($sql);
+ // return redirect()->to('/flagdef');
+ // } else {
+ // $sql = "UPDATE `flagdef` SET InstrumentID = $instrumentid,
+ // Flag='$flag',
+ // flagtext='$flagtext',
+ // flagdesc='$flagdesc',
+ // onscreen=$onscreen,
+ // onresult=$onresult
+ // WHERE FlagDefID=$flagdefid";
+ // $query = $db->query($sql);
+ // return redirect()->to('/flagdef');
+ // }
+
+ // } else {
+ // $data['validation'] = $this->validator;
+ // return view('flags_editor', $data);
+ // }
+
+ // } else {
+ // return view('flags_editor', $data);
+ // }
+
+ // return view('flags_editor', $data);
+ // }
+
+ // public function delete($FlagDefID) {
+ // if ($this->request->getMethod() === 'post') {
+ // $db = \Config\Database::connect();
+ // $sql = "DELETE FROM flagdef WHERE FlagDefID=$FlagDefID";
+ // $query = $db->query($sql);
+ // return redirect()->to('/flagdef');
+ // } else { // Redirect jika metode bukan POST
+ // return redirect()->to('/flagdef');
+ // }
+
+ // }
+}
diff --git a/app/Controllers/Equipment.php b/app/Controllers/Equipment.php
new file mode 100644
index 0000000..785bfed
--- /dev/null
+++ b/app/Controllers/Equipment.php
@@ -0,0 +1,92 @@
+query($sql);
+ // $results = $query->getResultArray();
+ // $data['insts'] = $results;
+ // }
+ // if ($this->request->getMethod() === 'post') {
+ // // $rules = [ 'instname' => 'required' ];
+ // // $instname = $this->request->getPost('instname');
+ // if($this->validate($rules)){
+ // if($instid == 0 ) {
+ // $db = \Config\Database::connect();
+ // $sql = "insert into dict_insts(instname, createdate) values ('$instname', NOW())";
+ // $query = $db->query($sql);
+ // return redirect()->to('/insts');
+ // } else {
+ // $db = \Config\Database::connect();
+ // $sql = "update dict_insts set instname='$instname' where instid='$instid'";
+ // $query = $db->query($sql);
+ // return redirect()->to('/insts');
+ // }
+ // } else {
+ // $data['validation'] = $this->validator;
+ // return view('insts_editor',$data);
+ // }
+ // } else {
+ // return view('equipment_editor');
+ // }
+
+ return view('equipment_editor');
+ }
+
+ public function detailInstrumentTest($EquipmentID) {
+
+ $data['EquipmentID'] = $EquipmentID;
+
+ $db = \Config\Database::connect();
+
+ $sql = "SELECT COUNT(*) as testcount
+ FROM patres
+ WHERE EquipmentID='$EquipmentID'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['testcount'] = $results[0]['testcount'];
+
+ $sql = "SELECT DATEDIFF(
+ MAX(ResultDateTime),
+ MIN(ResultDateTime)) as days,
+ MIN(ResultDateTime) as firstdate,
+ MAX(ResultDateTime) as lastdate
+ FROM patres
+ WHERE EquipmentID='$EquipmentID';";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['days'] = $results[0]['days'];
+ $data['firstdate'] = $results[0]['firstdate'];
+ $data['lastdate'] = $results[0]['lastdate'];
+
+ $sql = "SELECT YEAR(ResultDateTime) AS year, MONTH(ResultDateTime) AS month, COUNT(*) AS count
+ FROM patres
+ WHERE EquipmentID = '$EquipmentID'
+ GROUP BY YEAR(ResultDateTime), MONTH(ResultDateTime)
+ ORDER BY YEAR(ResultDateTime), MONTH(ResultDateTime);";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['counts'] = $results;
+
+ $sql = "SELECT SampleID,TestTechCode,Result,ResultDateTime,CreateDate FROM patres
+ WHERE EquipmentID='$EquipmentID'
+ ORDER BY ResultID DESC
+ LIMIT 3000";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['testData'] = $results;
+
+ return view('equipment_detail_test', $data);
+ }
+}
diff --git a/app/Controllers/Flags.php b/app/Controllers/Flags.php
new file mode 100644
index 0000000..78a1a52
--- /dev/null
+++ b/app/Controllers/Flags.php
@@ -0,0 +1,112 @@
+ 'https://services-summit.my.id/api/getProductAlias',
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+ CURLOPT_CUSTOMREQUEST => 'GET',
+ ));
+
+ $response = curl_exec($curl);
+ curl_close($curl);
+ $data = json_decode($response, true);
+
+ return $data;
+ }
+
+ public function index() {
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM flagdef";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['flagdef'] = $results;
+
+ return view('flags_index.php', $data);
+ }
+
+ public function view($FlagDefID) {
+ $FlagDefID = (int) $FlagDefID;
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM flagdef WHERE FlagDefID=$FlagDefID";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['flagdef'] = $results;
+
+ return view('flags_view.php', $data);
+ }
+
+ public function edit($FlagDefID) {
+ $data = array();
+ $data['instsAlias'] = $this->getProductAlias();
+
+ $db = \Config\Database::connect();
+
+ if ($FlagDefID != 0) {
+ $sql = "SELECT * FROM flagdef where FlagDefID='$FlagDefID'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['flagdef'] = $results;
+ }
+
+ if ($this->request->getMethod() === 'post') {
+
+ $rules = [ 'flagtext' => 'required'];
+
+ $flagdefid = $this->request->getPost('flagdefid');
+ $instrumentid = $this->request->getPost('instrumentid');
+ $flag = $this->request->getPost('flag');
+ $flagtext = $this->request->getPost('flagtext');
+ $flagdesc = $this->request->getPost('flagdesc');
+ $onscreen = $this->request->getPost('onscreen');
+ $onresult = $this->request->getPost('onresult');
+
+ if ($this->validate($rules)){
+
+ if ($flagdefid == 0 ) {
+ $sql = "INSERT INTO `flagdef` ( `InstrumentID`, `Flag`, `FlagText`, `FlagDesc`, `OnScreen`, `OnResult`, `CreateDate` )
+ VALUES ( $instrumentid, '$flag', '$flagtext', '$flagdesc', $onscreen, $onresult ,NOW())";
+ $query = $db->query($sql);
+ return redirect()->to('/flagdef');
+ } else {
+ $sql = "UPDATE `flagdef` SET InstrumentID = $instrumentid,
+ Flag='$flag',
+ flagtext='$flagtext',
+ flagdesc='$flagdesc',
+ onscreen=$onscreen,
+ onresult=$onresult
+ WHERE FlagDefID=$flagdefid";
+ $query = $db->query($sql);
+ return redirect()->to('/flagdef');
+ }
+
+ } else {
+ $data['validation'] = $this->validator;
+ return view('flags_editor', $data);
+ }
+
+ } else {
+ return view('flags_editor', $data);
+ }
+
+ return view('flags_editor', $data);
+ }
+
+ public function delete($FlagDefID) {
+ if ($this->request->getMethod() === 'post') {
+ $db = \Config\Database::connect();
+ $sql = "DELETE FROM flagdef WHERE FlagDefID=$FlagDefID";
+ $query = $db->query($sql);
+ return redirect()->to('/flagdef');
+ } else { // Redirect jika metode bukan POST
+ return redirect()->to('/flagdef');
+ }
+
+ }
+}
diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php
new file mode 100644
index 0000000..f4f4c37
--- /dev/null
+++ b/app/Controllers/Home.php
@@ -0,0 +1,109 @@
+ 'https://services-summit.my.id/api/getProductSites',
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+ CURLOPT_CUSTOMREQUEST => 'GET',
+ ));
+
+ $response = curl_exec($curl);
+ curl_close($curl);
+ $data = json_decode($response, true);
+
+ return $data;
+ }
+
+ public function dashboard() {
+
+ $db = \Config\Database::connect();
+ $productList = $this->getProductList(); //Get From API CRM
+
+ // Mengelompokkan Berdasarkan Jenis Product. cth(TMS30i, TMS50, dll)
+ foreach($productList as $item) {
+
+ // Mengelompokkan Data Khusus TMS30i/Medisys
+ if ( in_array($item["productaliasid"], [20, 60]) ) {
+ $productList30i[] = [
+ "productaliasid" => $item["productaliasid"],
+ "productnumber" => $item["productnumber"],
+ "sitename" => $item["sitename"]
+ ];
+
+ // Mengelompokkan Data Khusus 1024i
+ } else if ($item["productaliasid"] == 18) {
+ $productList1024i[] = [
+ "productaliasid" => $item["productaliasid"],
+ "productnumber" => $item["productnumber"],
+ "sitename" => $item["sitename"]
+ ];
+
+ // Mengelompokkan Data Khusus 24i
+ } else if ($item["productaliasid"] == 19) {
+ $productList24i[] = [
+ "productaliasid" => $item["productaliasid"],
+ "productnumber" => $item["productnumber"],
+ "sitename" => $item["sitename"]
+ ];
+
+ // Mengelompokkan Data Khusus 50i
+ } else if ($item["productaliasid"] == 21) {
+ $productList50i[] = [
+ "productaliasid" => $item["productaliasid"],
+ "productnumber" => $item["productnumber"],
+ "sitename" => $item["sitename"]
+ ];
+ } else {
+ $productList30i = null;
+ $productList1024i = null;
+ $productList24i = null;
+ $productList50i = null;
+ }
+ }
+
+ $sql = "SELECT EquipmentID, COUNT(*) AS patresCount, MAX(ResultDateTime) AS lastResultDate
+ FROM patres
+ GROUP BY EquipmentID
+ ORDER By patresCount DESC";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+
+ // Menyamakan Data CRM dan CLQMS Berdasarkan SN Number
+ foreach ($results as $itemDB) {
+ foreach ($productList30i as $itemCRM) {
+
+ if ($itemDB['EquipmentID'] == $itemCRM['productnumber']) {
+ // echo " Product Alias ID CRM: " . $itemCRM["productaliasid"] . " ";
+ // echo "Product Number: " . $itemCRM["productnumber"] . " ";
+ // echo "Site Name: " . $itemCRM["sitename"] . " ";
+ // echo "Product Alias ID DB: " . $itemDB["EquipmentID"] . " ";
+ // echo "Product Number: " . $itemDB["patresCount"] . " ";
+ // echo "Site Name: " . $itemDB["lastResultDate"] . " ";
+ // echo "---------------------------- ";
+ $tms30i[] = [
+ "productaliasid" => $itemCRM["productaliasid"],
+ "EquipmentID" => $itemDB["EquipmentID"],
+ "sitename" => $itemCRM["sitename"],
+ "patresCount" => $itemDB["patresCount"],
+ "lastResultDate" => $itemDB["lastResultDate"]
+ ];
+
+ }
+
+ }
+
+ }
+
+ $data['tms30i'] = $tms30i;
+
+ return view('home_dashboard.php', $data);
+ }
+
+}
diff --git a/app/Controllers/Insts.php b/app/Controllers/Insts.php
new file mode 100644
index 0000000..2f6eda6
--- /dev/null
+++ b/app/Controllers/Insts.php
@@ -0,0 +1,48 @@
+query($sql);
+ $results = $query->getResultArray();
+ $data['insts'] = $results;
+ return view('insts_index.php', $data);
+ }
+
+ public function edit($instid) {
+ $data = array();
+ if ($instid != 0) {
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM dict_insts where instid='$instid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['insts'] = $results;
+ }
+ if ($this->request->getMethod() === 'post') {
+ $rules = [ 'instname' => 'required' ];
+ $instname = $this->request->getPost('instname');
+ if($this->validate($rules)){
+ if($instid == 0 ) {
+ $db = \Config\Database::connect();
+ $sql = "insert into dict_insts(instname, createdate) values ('$instname', NOW())";
+ $query = $db->query($sql);
+ return redirect()->to('/insts');
+ } else {
+ $db = \Config\Database::connect();
+ $sql = "update dict_insts set instname='$instname' where instid='$instid'";
+ $query = $db->query($sql);
+ return redirect()->to('/insts');
+ }
+ } else {
+ $data['validation'] = $this->validator;
+ return view('insts_editor',$data);
+ }
+ } else {
+ return view('insts_editor', $data);
+ }
+ }
+}
diff --git a/app/Controllers/ProdInst.php b/app/Controllers/ProdInst.php
new file mode 100644
index 0000000..6731b7e
--- /dev/null
+++ b/app/Controllers/ProdInst.php
@@ -0,0 +1,186 @@
+ 'http://summitcrm.local/api/getProductList',
+ CURLOPT_URL => 'https://services-summit.my.id/api/getProductList',
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+ CURLOPT_CUSTOMREQUEST => 'GET',
+ ));
+
+ $response = curl_exec($curl);
+ curl_close($curl);
+ $data = json_decode($response, true);
+
+ return $data;
+ }
+
+ public function index() {
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM prodinst";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['prodinsts'] = $results;
+ return view('prodinst_index.php', $data);
+ }
+
+ public function detail($prodinstid) {
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM prodinst where prodinstid='$prodinstid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['prodinst'] = $results;
+
+ $sql = "SELECT COUNT(*) as testcount FROM patres WHERE prodinstid='$prodinstid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['testcount'] = $results[0]['testcount'];
+
+ $sql = "SELECT DATEDIFF(MAX(resultdate), MIN(resultdate)) as days, min(resultdate) as firstdate, max(resultdate) as lastdate FROM patres WHERE prodinstid='$prodinstid';";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['days'] = $results[0]['days'];
+ $data['firstdate'] = $results[0]['firstdate'];
+ $data['lastdate'] = $results[0]['lastdate'];
+
+ $sql = "SELECT MONTH(resultdate) AS month, COUNT(*) AS count
+ FROM patres
+ GROUP BY MONTH(resultdate) limit 8";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['counts'] = $results;
+
+ return view('prodinst_detail.php', $data);
+ }
+
+ public function edit($prodinstid) {
+ $data = array();
+ $db = \Config\Database::connect();
+ if ($prodinstid != 0) {
+ $sql = "SELECT * FROM prodinst where prodinstid='$prodinstid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['prodinsts'] = $results;
+ }
+ $sql = "SELECT * FROM dict_insts";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['insts'] = $results;
+
+ if ($this->request->getMethod() === 'post') {
+ $rules = [ 'prodinstcode' => 'required' ];
+ $productid = $this->request->getPost('productid');
+ $instid = $this->request->getPost('instid');
+ $prodinstname = $this->request->getPost('prodinstname');
+ $prodinstcode = $this->request->getPost('prodinstcode');
+ $prodinstkey= $this->request->getPost('prodinstkey');
+ if($this->validate($rules)){
+ if($prodinstid == 0 ) {
+ $sql = "insert into prodinst(productid, instid, prodinstname, prodinstcode, prodinstkey, createdate) values ('$productid', '$instid', '$prodinstname', '$prodinstcode', '$prodinstkey', NOW())";
+ $query = $db->query($sql);
+ return redirect()->to('/prodinst');
+ } else {
+ $sql = "update prodinst set productid='$productid', instid='$instid', prodinstname='$prodinstname', prodinstcode='$prodinstcode', prodinstkey='$prodinstkey' where prodinstid='$prodinstid'";
+ $query = $db->query($sql);
+ return redirect()->to('/prodinst');
+ }
+ } else {
+ $data['validation'] = $this->validator;
+ $data['products'] = $this->getProductList();
+ return view('prodinst_editor',$data);
+ }
+ } else {
+ $data['products'] = $this->getProductList();
+ return view('prodinst_editor', $data);
+ }
+ }
+
+ public function prodinsttest_edit($prodinstid) {
+ $data = array();
+ $data['prodinstid'] = $prodinstid;
+
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM prodinst where prodinstid='$prodinstid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['prodinsts'] = $results;
+
+ $sql = "SELECT * FROM prodinst_test where prodinstid='$prodinstid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['prodinsttests'] = $results;
+
+ $sql = "SELECT * FROM dict_tests";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['tests'] = $results;
+
+ return view('prodinst_test_editor', $data);
+ }
+
+ public function prodinsttest_update() {
+ $db = \Config\Database::connect();
+ $prodinstid = $this->request->getPost('prodinstid');
+ $testids = $this->request->getPost('testid');
+ $prodinsttestcodes = $this->request->getPost('prodinsttestcode');
+ $deleteid = $this->request->getPost('deleteid');
+
+ // from form
+ $test = array();
+ foreach($testids as $key => $testid) {
+ if($testid != '') { $test[$testid] = $prodinsttestcodes[$key]; }
+ }
+
+ $sql = "SELECT * FROM prodinst_test where prodinstid='$prodinstid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data = $results;
+ $qtest = array();
+ foreach($data as $qdata) {
+ $qtest[$qdata['testid']] = $qdata['prodinsttestcode'];
+ }
+
+ echo "";
+ print_r($test);
+ print_r($qtest);
+ echo " ";
+
+ $sqlinsert = "";
+ foreach($test as $qtestid => $qtestcode) {
+ if( !isset($qtest[$qtestid]) ) { //if none insert
+ $sqlinsert .= "('$prodinstid', '$qtestid', '$qtestcode'),";
+ } else if( $qtest[$qtestid]!= $qtestcode ) {
+ $sqlupdate = "update prodinst_test set prodinsttestcode='$qtestcode' where prodinstid='$prodinstid' and testid='$qtestid'";
+ echo " $sqlupdate";
+ $db->query($sqlupdate);
+ }
+ }
+
+ $sqlinsert = rtrim($sqlinsert,',');
+ if($sqlinsert != '') {
+ $sqlinsert = "INSERT INTO prodinst_test(prodinstid, testid, prodinsttestcode) values $sqlinsert ";
+ echo " $sqlinsert";
+ $db->query($sqlinsert);
+ }
+
+ $sqldelete = "";
+ foreach($qtest as $qtestid => $qtestcode) {
+ if( !isset($test[$qtestid]) ) { //if none delete
+ $sqldelete.= "'$qtestid',";
+ }
+ }
+ if($sqldelete != '') {
+ $sqldelete = rtrim($sqldelete,',');
+ $sqldelete = "delete from prodinst_test where prodinstid='$prodinstid' and testid in ($sqldelete)";
+ echo " $sqldelete";
+ $db->query($sqldelete);
+ }
+ }
+}
diff --git a/app/Controllers/Reports.php b/app/Controllers/Reports.php
new file mode 100644
index 0000000..8066092
--- /dev/null
+++ b/app/Controllers/Reports.php
@@ -0,0 +1,235 @@
+ 'https://services-summit.my.id/api/getProductSites',
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+ CURLOPT_CUSTOMREQUEST => 'GET',
+ ));
+
+ $response = curl_exec($curl);
+ curl_close($curl);
+ $data = json_decode($response, true);
+
+ return $data;
+ }
+
+ public function rangeData () {
+
+ $startdate = $this->request->getGet('startdate');
+ $enddate = $this->request->getGet('enddate');
+
+ if ($startdate && $enddate) {
+ $db = \Config\Database::connect();
+
+ // $sql = "SELECT
+ // MIN(YEAR(ResultDateTime)) AS FirstYear,
+ // MAX(YEAR(ResultDateTime)) AS LastYear
+ // FROM patres";
+ // $query = $db->query($sql);
+ // $results = $query->getResultArray();
+ $yearRange = range(date('Y', strtotime($startdate)), date('Y', strtotime($enddate)));
+
+ $query = $db->query("
+ SELECT EquipmentID, YEAR(ResultDateTime) AS Tahun, COUNT(*) AS patresCount
+ FROM patres
+ WHERE ResultDateTime BETWEEN '$startdate 00:00:01' AND '$enddate 23:59:59'
+ GROUP BY YEAR(ResultDateTime), EquipmentID
+ ORDER BY EquipmentID, tahun
+ ");
+ $rows = $query->getResultArray();
+
+ $equipments = [
+ ['EquipmentID' => '6006100619', 'sitename' => 'RS Permata Cibubur'],
+ ['EquipmentID' => '6005840519', 'sitename' => 'RS dr. Oen Kandang Sapi Solo'],
+ ['EquipmentID' => '6008850621', 'sitename' => 'RS Universitas Sebelas Maret'],
+ ['EquipmentID' => '6010610522', 'sitename' => 'RSUD Muntilan'],
+ ['EquipmentID' => '6011310722', 'sitename' => 'National Hospital Surabaya'],
+ ['EquipmentID' => '6011320722', 'sitename' => 'RS Mardi Rahayu Kudus'],
+ ['EquipmentID' => '6002771117', 'sitename' => 'UPTD Laboratorium Kesehatan Kota Magelang'],
+ ['EquipmentID' => '6014341023', 'sitename' => 'RSUD Sukoharjo'],
+ ['EquipmentID' => '6015090124', 'sitename' => 'Persada Hospital'],
+ ['EquipmentID' => '6015560324', 'sitename' => 'ScanMe Labs Jakarta Kelapa Gading'],
+ ['EquipmentID' => '6007621020', 'sitename' => 'UPT Labkesmas Kabupaten Magelang'],
+ ['EquipmentID' => '6016560724', 'sitename' => 'RS Kasih Ibu Surakarta'],
+ ['EquipmentID' => '6016850924', 'sitename' => 'Laboratorium Klinik Budi Sehat']
+ ];
+
+ // Mapping EquipmentID => sitename
+ $siteNames = [];
+ foreach ($equipments as $eq) {
+ $siteNames[(int)$eq['EquipmentID']] = $eq['sitename'];
+ }
+
+ // Buat struktur pivot
+ $pivot = [];
+ foreach ($rows as $row) {
+ $eid = (int) $row['EquipmentID'];
+ $tahun = (int) $row['Tahun'];
+ $count = (int) $row['patresCount'];
+
+ if (!isset($pivot[$eid])) {
+ foreach ($yearRange as $y) {
+ $pivot[$eid][$y] = 0;
+ }
+ }
+
+ $pivot[$eid][$tahun] = $count;
+ }
+
+ // Buat struktur Growth/Presentase
+ $growth = [];
+ foreach ($pivot as $eid => $data) {
+ foreach ($yearRange as $i => $currYear) {
+ if ($i === 0) continue;
+
+ $prevYear = $yearRange[$i - 1];
+ $prev = $data[$prevYear] ?? 0;
+ $curr = $data[$currYear] ?? 0;
+
+ $key = "$prevYear-$currYear";
+
+ if (!isset($growth[$eid])) {
+ $growth[$eid] = [];
+ }
+
+ if ($prev == 0) {
+ $growth[$eid][$key] = '-';
+ } else {
+ $percent = (($curr - $prev) / $prev) * 100;
+ $growth[$eid][$key] = round($percent, 1) . '%';
+ }
+ }
+ }
+
+ return view('report_all_range', [
+ 'yearRange' => $yearRange,
+ 'pivot' => $pivot,
+ 'growth' => $growth,
+ 'siteNames' => $siteNames
+ ]);
+
+ } else {
+ return view('report_all_range');
+ }
+
+ }
+
+ public function spesificData() {
+
+ $first_month = (int)$this->request->getGet('first_month');
+ $last_month = (int)$this->request->getGet('last_month');
+
+ if ($first_month && $last_month) {
+
+ $db = \Config\Database::connect();
+
+ $sql = "SELECT
+ MIN(YEAR(ResultDateTime)) AS FirstYear,
+ MAX(YEAR(ResultDateTime)) AS LastYear
+ FROM patres";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $yearRange = range($results[0]['FirstYear'], $results[0]['LastYear']);
+
+ $query = $db->query("
+ SELECT EquipmentID, YEAR(ResultDateTime) AS Tahun, COUNT(*) AS patresCount
+ FROM patres
+ WHERE MONTH(ResultDateTime) BETWEEN $first_month AND $last_month
+ GROUP BY EquipmentID, YEAR(ResultDateTime)
+ ORDER BY EquipmentID, Tahun
+ ");
+
+ $rows = $query->getResultArray();
+
+ $equipments = [
+ ['EquipmentID' => '6006100619', 'sitename' => 'RS Permata Cibubur'],
+ ['EquipmentID' => '6005840519', 'sitename' => 'RS dr. Oen Kandang Sapi Solo'],
+ ['EquipmentID' => '6008850621', 'sitename' => 'RS Universitas Sebelas Maret'],
+ ['EquipmentID' => '6010610522', 'sitename' => 'RSUD Muntilan'],
+ ['EquipmentID' => '6011310722', 'sitename' => 'National Hospital Surabaya'],
+ ['EquipmentID' => '6011320722', 'sitename' => 'RS Mardi Rahayu Kudus'],
+ ['EquipmentID' => '6002771117', 'sitename' => 'UPTD Laboratorium Kesehatan Kota Magelang'],
+ ['EquipmentID' => '6014341023', 'sitename' => 'RSUD Sukoharjo'],
+ ['EquipmentID' => '6015090124', 'sitename' => 'Persada Hospital'],
+ ['EquipmentID' => '6015560324', 'sitename' => 'ScanMe Labs Jakarta Kelapa Gading'],
+ ['EquipmentID' => '6007621020', 'sitename' => 'UPT Labkesmas Kabupaten Magelang'],
+ ['EquipmentID' => '6016560724', 'sitename' => 'RS Kasih Ibu Surakarta'],
+ ['EquipmentID' => '6016850924', 'sitename' => 'Laboratorium Klinik Budi Sehat']
+ ];
+
+ // Mapping EquipmentID => sitename
+ $siteNames = [];
+ foreach ($equipments as $eq) {
+ $siteNames[(int)$eq['EquipmentID']] = $eq['sitename'];
+ }
+
+ // Buat struktur pivot
+ $pivot = [];
+ foreach ($rows as $row) {
+ $eid = (int) $row['EquipmentID'];
+ $tahun = (int) $row['Tahun'];
+ $count = (int) $row['patresCount'];
+
+ if (!isset($pivot[$eid])) {
+ foreach ($yearRange as $y) {
+ $pivot[$eid][$y] = 0;
+ }
+ }
+
+ $pivot[$eid][$tahun] = $count;
+ }
+
+ // Buat struktur Growth/Presentase
+ $growth = [];
+ foreach ($pivot as $eid => $data) {
+ foreach ($yearRange as $i => $currYear) {
+ if ($i === 0) continue;
+
+ $prevYear = $yearRange[$i - 1];
+ $prev = $data[$prevYear] ?? 0;
+ $curr = $data[$currYear] ?? 0;
+
+ $key = "$prevYear-$currYear";
+
+ if (!isset($growth[$eid])) {
+ $growth[$eid] = [];
+ }
+
+ if ($prev == 0) {
+ $growth[$eid][$key] = '-';
+ } else {
+ $percent = (($curr - $prev) / $prev) * 100;
+ $growth[$eid][$key] = round($percent, 1) . '%';
+ }
+ }
+ }
+
+ // $first_month = 7;
+ $name_first_month = DateTime::createFromFormat('!m', $first_month)->format('F');
+ $name_last_month = DateTime::createFromFormat('!m', $last_month)->format('F');
+
+ return view('report_spesific_range', [
+ 'yearRange' => $yearRange,
+ 'pivot' => $pivot,
+ 'growth' => $growth,
+ 'siteNames' => $siteNames,
+ 'name_first_month' => $name_first_month,
+ 'name_last_month' => $name_last_month
+ ]);
+
+ } else {
+ return view('report_spesific_range');
+ }
+
+
+ }
+
+}
diff --git a/app/Controllers/SecurityController.php b/app/Controllers/SecurityController.php
new file mode 100644
index 0000000..c404c2d
--- /dev/null
+++ b/app/Controllers/SecurityController.php
@@ -0,0 +1,56 @@
+ "?u=49250ad57372089f82a77ed99&id=d33314635a?u=49250ad57372089f82a77",
+ "password" => "MpSwZvr-CYOX4-EPsMmAsQQ&ved=0ahUKEwj636mKvt2HAxWDyzgGHbAkIEYQ4dUDCBA"
+ ];
+
+ if ($username === $auth_access['username'] && $password === $auth_access['password']) {
+ return True;
+ } else {
+ return False;
+ }
+ }
+
+ public function checksum_check($instrument_data, $json_checksum) {
+
+ // Hitung nilai checksum dari json_data
+ $computed_checksum = hash('sha256', $instrument_data);
+
+ // Verifikasi checksum
+ if ($computed_checksum === $json_checksum) {
+ // Sukses
+ return True;
+ } else {
+ // Gagal
+ return False;
+ }
+ }
+
+ public function decryptData($json_data, $key=null) {
+
+ // Menerima data terenkripsi dari permintaan POST
+ $encryptedData = $json_data;
+
+ // Kunci yang sama yang digunakan untuk enkripsi di sisi Python
+ $key = 'summit4ska1sakti';
+
+ // Mendekripsi data
+ $decryptedData = openssl_decrypt($encryptedData, 'aes-128-cbc', $key, 0, '');
+ if ($decryptedData === False) {
+ return False;
+ }
+
+ return $decryptedData;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/app/Controllers/Techs.php b/app/Controllers/Techs.php
new file mode 100644
index 0000000..d4d52cd
--- /dev/null
+++ b/app/Controllers/Techs.php
@@ -0,0 +1,71 @@
+query($sql);
+ $results = $query->getResultArray();
+ $data['techs'] = $results;
+ return view('techs_index.php', $data);
+ }
+
+ public function techinst_index($instid) {
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM dict_techs where instid=$instid";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['techs'] = $results;
+ $sql = "SELECT * FROM dict_insts where instid=$instid";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['insts'] = $results;
+ $data['instid'] = $instid;
+ return view('techinst_index.php', $data);
+ }
+
+ public function edit($instid, $techid) {
+ $data = array();
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM dict_techs";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['insts'] = $results;
+ $data['instid'] = $instid;
+ if ($flagid != 0) {
+ $sql = "SELECT * FROM dict_techs where techid='$techid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['techs'] = $results;
+ }
+ if ($this->request->getMethod() === 'post') {
+ $rules = [ 'techtext' => 'required', 'techinst' => 'required' ];
+ $instname = $this->request->getPost('instname');
+ $techinst = $this->request->getPost('techinst');
+ $techtext = $this->request->getPost('techtext');
+ $techdesc = $this->request->getPost('techdesc');
+ $onscreen = $this->request->getPost('onscreen');
+ $onresult = $this->request->getPost('onresult');
+ if($this->validate($rules)){
+ if($instid == 0 ) {
+ $sql = "INSERT INTO `dict_techs` ( `instid`, `techinst`, `techtext`, `techdesc`, `onscreen`, `onresult`, `createdate` )
+ VALUES ( '$instid', '$flaginst', '$flagtext', '$flagdesc', '$onscreen', '$onresult' ,NOW())";
+ $query = $db->query($sql);
+ return redirect()->to('/techs/insts/$instid');
+ } else {
+ $sql = "update dict_techs set instid='$instid', techinst='$techinst', techtext='$techtext', techdesc='$techdesc', onscreen='$onscreen', onresult='$onresult' where techid='$techid'";
+ $query = $db->query($sql);
+ return redirect()->to('/techs/insts/$instid');
+ }
+ } else {
+ $data['validation'] = $this->validator;
+ return view('techs_editor',$data);
+ }
+ } else {
+ return view('techs_editor', $data);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/Controllers/Tests.php b/app/Controllers/Tests.php
new file mode 100644
index 0000000..ece4acd
--- /dev/null
+++ b/app/Controllers/Tests.php
@@ -0,0 +1,60 @@
+query($sql);
+ $results = $query->getResultArray();
+ $data['tests'] = $results;
+ return view('tests_index.php', $data);
+ }
+
+ public function edit($testid) {
+ $data = array();
+ if ($testid != 0) {
+ $db = \Config\Database::connect();
+ $sql = "SELECT * FROM dict_tests where testid='$testid'";
+ $query = $db->query($sql);
+ $results = $query->getResultArray();
+ $data['tests'] = $results;
+ }
+ if ($this->request->getMethod() === 'post') {
+ $rules = [
+ 'testcode' => 'required',
+ 'testname' => 'required'
+ ];
+ $testid = $this->request->getPost('testid');
+ $testcode = $this->request->getPost('testcode');
+ $testname = $this->request->getPost('testname');
+ $unit = $this->request->getPost('unit');
+ $method = $this->request->getPost('method');
+ $data['new_value'] = [
+ 'testcode' => $testcode,
+ 'testname' => $testname,
+ 'unit' => $unit,
+ 'method' => $method
+ ];
+ if($this->validate($rules)){
+ if($testid == 0 ) {
+ $db = \Config\Database::connect();
+ $sql = "insert into dict_tests(testcode,testname,unit,method) values ('$testcode', '$testname', '$unit', '$method')";
+ $query = $db->query($sql);
+ return redirect()->to('/tests');
+ } else {
+ $db = \Config\Database::connect();
+ $sql = "update dict_tests set testcode='$testcode', testname='$testname', unit='$unit', method='$method' where testid='$testid'";
+ $query = $db->query($sql);
+ return redirect()->to('/tests');
+ }
+ } else {
+ $data['validation'] = $this->validator;
+ return view('tests_editor',$data);
+ }
+ }
+ return view('tests_editor', $data);
+ }
+}
diff --git a/app/Database/Migrations/.gitkeep b/app/Database/Migrations/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Database/Seeds/.gitkeep b/app/Database/Seeds/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Filters/.gitkeep b/app/Filters/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Helpers/.gitkeep b/app/Helpers/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Language/.gitkeep b/app/Language/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/Language/en/Validation.php b/app/Language/en/Validation.php
new file mode 100644
index 0000000..54d1e7a
--- /dev/null
+++ b/app/Language/en/Validation.php
@@ -0,0 +1,4 @@
+extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
New Query
+
+
+
+
+
+ No.
+ Name
+ Instument
+ Status
+ Action
+
+
+
+
+ 1
+ Get Data Test
+ TMS30i
+ active
+
+
+
+
+
+
+
+ 2
+ Send Message Every Hour
+ TMS30i
+ inactive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/equipment_detail_test.php b/app/Views/equipment_detail_test.php
new file mode 100644
index 0000000..a480081
--- /dev/null
+++ b/app/Views/equipment_detail_test.php
@@ -0,0 +1,273 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('css') ?>
+
+= $this->endSection() ?>
+
+= $this->section('content') ?>
+format('D, d M Y - H:i:s.v');
+
+ $dateObject = new DateTime($lastdate);
+ $formatedLastDate = $dateObject->format('D, d M Y - H:i:s.v');
+
+ // $startDate = new DateTime($firstdate);
+ // $endDate = new DateTime($lastdate);
+ // $interval = $startDate->diff($endDate);
+ // $months = $interval->y * 12 + $interval->m;
+ // // Jika ada bagian bulan yang tidak lengkap, tambahkan satu bulan
+ // if ($interval->d > 0) {
+ // $months++;
+ // }
+ // $testpermonth = $testcount / $months;
+ // $testpermonth = number_format($testpermonth, 2, ',', '');
+ $testpermonth = number_format($testcount / ($days / 30), 2, ',', '');
+ $testperday = number_format($testcount / $days, 2, ',', '');
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Test Count
+
+
+
=$testcount;?>
+ Total Test
+
+
+
+
+
+
+
+
+
+
+
+
Monthly (avg)
+
+
+
=$testpermonth;?>
+ Test per month
+
+
+
+
+
+
+
+
+
+
Daily (avg)
+
+
+
=$testperday;?>
+ Test per day
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Detail Chart
+
+
+
+
+
+
+ Tanggal Awal : =$formatedFirstDate;?>
+
+
+ Tanggal Akhir : =$formatedLastDate;?>
+
+
+
+
+
+
+
+
+
+
+
+
Detail Test
+
+
+
+
+
+
+
+ #
+ Sample ID
+ Test Code
+ Result
+ Result Date
+ Create Date
+
+
+
+ ";
+ echo "$i ";
+ echo "$sampleid ";
+ echo "$testtechcode ";
+ echo "$result ";
+ echo "$resultdatetime ";
+ echo "$createdate ";
+ echo "";
+
+ $i++;
+ } ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/equipment_editor.php b/app/Views/equipment_editor.php
new file mode 100644
index 0000000..b6a6dd4
--- /dev/null
+++ b/app/Views/equipment_editor.php
@@ -0,0 +1,152 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Flag Editor
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
diff --git a/app/Views/equipment_index.php b/app/Views/equipment_index.php
new file mode 100644
index 0000000..e678746
--- /dev/null
+++ b/app/Views/equipment_index.php
@@ -0,0 +1,89 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Instruments List
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+ Equipment ID
+ Department ID
+ Instrument Name
+ Status/Enable
+ Equipment Role
+ Create Date
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/errors/cli/error_404.php b/app/Views/errors/cli/error_404.php
new file mode 100644
index 0000000..456ea3e
--- /dev/null
+++ b/app/Views/errors/cli/error_404.php
@@ -0,0 +1,7 @@
+getFile()) . ':' . $exception->getLine(), 'green'));
+CLI::newLine();
+
+$last = $exception;
+
+while ($prevException = $last->getPrevious()) {
+ $last = $prevException;
+
+ CLI::write(' Caused by:');
+ CLI::write(' [' . get_class($prevException) . ']', 'red');
+ CLI::write(' ' . $prevException->getMessage());
+ CLI::write(' at ' . CLI::color(clean_path($prevException->getFile()) . ':' . $prevException->getLine(), 'green'));
+ CLI::newLine();
+}
+
+// The backtrace
+if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
+ $backtraces = $last->getTrace();
+
+ if ($backtraces) {
+ CLI::write('Backtrace:', 'green');
+ }
+
+ foreach ($backtraces as $i => $error) {
+ $padFile = ' '; // 4 spaces
+ $padClass = ' '; // 7 spaces
+ $c = str_pad($i + 1, 3, ' ', STR_PAD_LEFT);
+
+ if (isset($error['file'])) {
+ $filepath = clean_path($error['file']) . ':' . $error['line'];
+
+ CLI::write($c . $padFile . CLI::color($filepath, 'yellow'));
+ } else {
+ CLI::write($c . $padFile . CLI::color('[internal function]', 'yellow'));
+ }
+
+ $function = '';
+
+ if (isset($error['class'])) {
+ $type = ($error['type'] === '->') ? '()' . $error['type'] : $error['type'];
+ $function .= $padClass . $error['class'] . $type . $error['function'];
+ } elseif (! isset($error['class']) && isset($error['function'])) {
+ $function .= $padClass . $error['function'];
+ }
+
+ $args = implode(', ', array_map(static function ($value) {
+ switch (true) {
+ case is_object($value):
+ return 'Object(' . get_class($value) . ')';
+
+ case is_array($value):
+ return count($value) ? '[...]' : '[]';
+
+ case $value === null:
+ return 'null'; // return the lowercased version
+
+ default:
+ return var_export($value, true);
+ }
+ }, array_values($error['args'] ?? [])));
+
+ $function .= '(' . $args . ')';
+
+ CLI::write($function);
+ CLI::newLine();
+ }
+}
diff --git a/app/Views/errors/cli/production.php b/app/Views/errors/cli/production.php
new file mode 100644
index 0000000..7db744e
--- /dev/null
+++ b/app/Views/errors/cli/production.php
@@ -0,0 +1,5 @@
+
+
+
+
+ = lang('Errors.pageNotFound') ?>
+
+
+
+
+
+
404
+
+
+
+ = nl2br(esc($message)) ?>
+
+ = lang('Errors.sorryCannotFind') ?>
+
+
+
+
+
diff --git a/app/Views/errors/html/error_exception.php b/app/Views/errors/html/error_exception.php
new file mode 100644
index 0000000..406b48e
--- /dev/null
+++ b/app/Views/errors/html/error_exception.php
@@ -0,0 +1,418 @@
+
+
+
+
+
+
+
+ = esc($title) ?>
+
+
+
+
+
+
+
+
+
+
+
+
= esc(clean_path($file)) ?> at line = esc($line) ?>
+
+
+
+ = static::highlightFile($file, $line, 15); ?>
+
+
+
+
+
+ getPrevious()) {
+ $last = $prevException;
+ ?>
+
+
+ Caused by:
+ = esc(get_class($prevException)), esc($prevException->getCode() ? ' #' . $prevException->getCode() : '') ?>
+
+ = nl2br(esc($prevException->getMessage())) ?>
+ getMessage())) ?>"
+ rel="noreferrer" target="_blank">search →
+ = esc(clean_path($prevException->getFile()) . ':' . $prevException->getLine()) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $row) : ?>
+
+
+
+
+
+
+
+ {PHP internal code}
+
+
+
+
+ — = esc($row['class'] . $row['type'] . $row['function']) ?>
+
+
+ ( arguments )
+
+
+
+ getParameters();
+ }
+
+ foreach ($row['args'] as $key => $value) : ?>
+
+ = esc(isset($params[$key]) ? '$' . $params[$key]->name : "#{$key}") ?>
+ = esc(print_r($value, true)) ?>
+
+
+
+
+
+
+ ()
+
+
+
+
+ — = esc($row['function']) ?>()
+
+
+
+
+
+
+ = static::highlightFile($row['file'], $row['line']) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
$= esc($var) ?>
+
+
+
+
+ Key
+ Value
+
+
+
+ $value) : ?>
+
+ = esc($key) ?>
+
+
+ = esc($value) ?>
+
+ = esc(print_r($value, true)) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
Constants
+
+
+
+
+ Key
+ Value
+
+
+
+ $value) : ?>
+
+ = esc($key) ?>
+
+
+ = esc($value) ?>
+
+ = esc(print_r($value, true)) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Path
+ = esc($request->getUri()) ?>
+
+
+ HTTP Method
+ = esc(strtoupper($request->getMethod())) ?>
+
+
+ IP Address
+ = esc($request->getIPAddress()) ?>
+
+
+ Is AJAX Request?
+ = $request->isAJAX() ? 'yes' : 'no' ?>
+
+
+ Is CLI Request?
+ = $request->isCLI() ? 'yes' : 'no' ?>
+
+
+ Is Secure Request?
+ = $request->isSecure() ? 'yes' : 'no' ?>
+
+
+ User Agent
+ = esc($request->getUserAgent()->getAgentString()) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
$= esc($var) ?>
+
+
+
+
+ Key
+ Value
+
+
+
+ $value) : ?>
+
+ = esc($key) ?>
+
+
+ = esc($value) ?>
+
+ = esc(print_r($value, true)) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ No $_GET, $_POST, or $_COOKIE Information to show.
+
+
+
+
+ headers(); ?>
+
+
+
Headers
+
+
+
+
+ Header
+ Value
+
+
+
+
+
+ = esc($header->getName(), 'html') ?>
+ = esc($header->getValueLine(), 'html') ?>
+
+
+
+
+
+
+
+
+
+ setStatusCode(http_response_code());
+ ?>
+
+
+
+ Response Status
+ = esc($response->getStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
+
+
+
+ headers(); ?>
+
+
+
+
Headers
+
+
+
+
+ Header
+ Value
+
+
+
+
+
+ = esc($name, 'html') ?>
+ = esc($response->getHeaderLine($name), 'html') ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = esc(clean_path($file)) ?>
+
+
+
+
+
+
+
+
+
+
+ Memory Usage
+ = esc(static::describeMemory(memory_get_usage(true))) ?>
+
+
+ Peak Memory Usage:
+ = esc(static::describeMemory(memory_get_peak_usage(true))) ?>
+
+
+ Memory Limit:
+ = esc(ini_get('memory_limit')) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php
new file mode 100644
index 0000000..2f59a8d
--- /dev/null
+++ b/app/Views/errors/html/production.php
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ = lang('Errors.whoops') ?>
+
+
+
+
+
+
+
+
= lang('Errors.whoops') ?>
+
+
= lang('Errors.weHitASnag') ?>
+
+
+
+
+
+
diff --git a/app/Views/flaginst_index.php b/app/Views/flaginst_index.php
new file mode 100644
index 0000000..159a6d9
--- /dev/null
+++ b/app/Views/flaginst_index.php
@@ -0,0 +1,59 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Flags List For Instrument =$instname;?>
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+ ID Flag Text Flag Desc.
+
+
+
+
+
+ =$flagid;?> =$flagtext;?> =$flagdesc;?>
+
+ edit
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/flags_editor.php b/app/Views/flags_editor.php
new file mode 100644
index 0000000..2ea6aa2
--- /dev/null
+++ b/app/Views/flags_editor.php
@@ -0,0 +1,114 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Flag Editor
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
diff --git a/app/Views/flags_index.php b/app/Views/flags_index.php
new file mode 100644
index 0000000..8e6c6ed
--- /dev/null
+++ b/app/Views/flags_index.php
@@ -0,0 +1,91 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Flags List
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
Berisi Flag dari Tiap Alat/Instrumen
+
New Flag
+
+
+
+
+
+ ID Instrumen Flag Flag Text Flag Desc. Create Date Action
+
+
+
+
+
+ = $data["FlagDefID"] ?>
+ = $data["InstrumentID"] ?>
+ = $data["Flag"] ?>
+ = $data["FlagText"] ?>
+ = $data["FlagDesc"] ?>
+ = $data["CreateDate"] ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/flags_view.php b/app/Views/flags_view.php
new file mode 100644
index 0000000..abbe391
--- /dev/null
+++ b/app/Views/flags_view.php
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+ ID
+ =$FlagDefID ?>
+
+
+ Instrument
+ =$Instrumentid ?>
+
+
+ Flag
+ =$Flag ?>
+
+
+ Flag Text
+ =$FlagText ?>
+
+
+ Flag Desc
+ =$FlagDesc ?>
+
+
+ On Screen
+ =$OnScreen ?>
+
+
+ On Result
+ =$OnResult ?>
+
+
+ Create Date
+ =$CreateDate ?>
+
+
+
+
\ No newline at end of file
diff --git a/app/Views/home_dashboard.php b/app/Views/home_dashboard.php
new file mode 100644
index 0000000..27a21bd
--- /dev/null
+++ b/app/Views/home_dashboard.php
@@ -0,0 +1,372 @@
+= $this->extend('layouts/main.php') ?>
+
+
+
+= $this->section('css') ?>
+
+= $this->endSection() ?>
+
+= $this->section('content') ?>
+
+
+
Dashboard
+
+
+
+
+
+
+
+
+
TMS 24i Premium
+
+
+
+
+
+
+
+
+
+
TMS 50i Superior
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TMS 30i
+
+
+ TMS 1024i
+
+
+ TMS 24i Premium
+
+
+ TMS 50i Superior
+
+
+
+
+
+
+
+
+
+
+ $prodinstname $sampleid $resultdate ";
+ // echo " S/N : $instrument $sampleid $resultdate ";
+ // }
+ ?>
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/insts_editor.php b/app/Views/insts_editor.php
new file mode 100644
index 0000000..1fc3c27
--- /dev/null
+++ b/app/Views/insts_editor.php
@@ -0,0 +1,56 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Inst Editor
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
diff --git a/app/Views/insts_index.php b/app/Views/insts_index.php
new file mode 100644
index 0000000..405890a
--- /dev/null
+++ b/app/Views/insts_index.php
@@ -0,0 +1,102 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Instruments List
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+ ID Instrument Name
+
+
+
+
+
+ =$instid;?> =$instname;?>
+
+ edit
+ Flag
+ Tech.
+
+
+
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/layouts/_sidebar.php b/app/Views/layouts/_sidebar.php
new file mode 100644
index 0000000..539232f
--- /dev/null
+++ b/app/Views/layouts/_sidebar.php
@@ -0,0 +1,40 @@
+
\ No newline at end of file
diff --git a/app/Views/layouts/form.php b/app/Views/layouts/form.php
new file mode 100644
index 0000000..dd62f2d
--- /dev/null
+++ b/app/Views/layouts/form.php
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+ Summit CRM
+
+
+
+
+
+
+ = $this->renderSection('head'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ = $this->renderSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+ = $this->renderSection('script'); ?>
+
+
+
\ No newline at end of file
diff --git a/app/Views/layouts/main.php b/app/Views/layouts/main.php
new file mode 100644
index 0000000..3948cd8
--- /dev/null
+++ b/app/Views/layouts/main.php
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+ tiny
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = $this->renderSection('css'); ?>
+
+
+
+
+
+ = $this->include('layouts/_sidebar'); ?>
+
+ = $this->renderSection('content'); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = $this->renderSection('script'); ?>
+
+
+
\ No newline at end of file
diff --git a/app/Views/layouts/window.php b/app/Views/layouts/window.php
new file mode 100644
index 0000000..5eb7452
--- /dev/null
+++ b/app/Views/layouts/window.php
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+ Summit CRM
+
+
+
+ = $this->renderSection('head'); ?>
+
+
+
+
+
+ = $this->renderSection('content'); ?>
+
+
+
+
+ = $this->renderSection('script'); ?>
+
+
+
\ No newline at end of file
diff --git a/app/Views/prodinst_detail.php b/app/Views/prodinst_detail.php
new file mode 100644
index 0000000..b58b04f
--- /dev/null
+++ b/app/Views/prodinst_detail.php
@@ -0,0 +1,112 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
=$prodinstname;?>
+
+
+
+
+
+
+
Test Count
+
+
+
=$testcount;?>
+ Total test
+
+
+
+
+
+
+
+
+
+
Test Count (avg)
+
+
+
=$testpermonth;?>
+ Test per month
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Detail
+
+ First data : =$firstdate;?>
+ Last data : =$lastdate;?>
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/prodinst_editor.php b/app/Views/prodinst_editor.php
new file mode 100644
index 0000000..705b667
--- /dev/null
+++ b/app/Views/prodinst_editor.php
@@ -0,0 +1,111 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Inst Editor
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
diff --git a/app/Views/prodinst_index.php b/app/Views/prodinst_index.php
new file mode 100644
index 0000000..005eec5
--- /dev/null
+++ b/app/Views/prodinst_index.php
@@ -0,0 +1,98 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Product - Instruments List
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+ ID Instrument Name Instrument Code
+
+
+
+
+
+ =$prodinstid;?> =$prodinstname;?> =$prodinstcode;?>
+
+ edit
+ tests
+
+
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/prodinst_test_editor.php b/app/Views/prodinst_test_editor.php
new file mode 100644
index 0000000..12d665e
--- /dev/null
+++ b/app/Views/prodinst_test_editor.php
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/Views/report_all_range.php b/app/Views/report_all_range.php
new file mode 100644
index 0000000..471364d
--- /dev/null
+++ b/app/Views/report_all_range.php
@@ -0,0 +1,128 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Data counter TMS30i Periode : = date('j M Y', strtotime($_GET['startdate']))?> -
+ = date('j M Y', strtotime($_GET['enddate']))?>
+
+
+
+ SN#
+ SITE
+
+ = esc($year) ?>
+
+
+ = $yearRange[$i - 1] ?> v = $yearRange[$i] ?>
+
+
+
+
+ $data): ?>
+
+ = esc($eid) ?>
+ = esc($siteNames[$eid] ?? 'Unknown') ?>
+
+
+ = esc($data[$year]) ?>
+
+
+
+ ';
+ } else {
+ $colorClass = 'text-success fw-bold';
+ $symbol = ' ';
+ }
+ ?>
+ = $symbol . esc($percent) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/report_spesific_range.php b/app/Views/report_spesific_range.php
new file mode 100644
index 0000000..199bc23
--- /dev/null
+++ b/app/Views/report_spesific_range.php
@@ -0,0 +1,142 @@
+
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Data counter semua tahun hanya untuk bulan =$name_first_month?> - =$name_last_month?>
+
+
+
+ SN#
+ SITE
+
+ = esc($year) ?>
+
+
+ = $yearRange[$i - 1] ?> v = $yearRange[$i] ?>
+
+
+
+
+ $data): ?>
+
+ = esc($eid) ?>
+ = esc($siteNames[$eid] ?? 'Unknown') ?>
+
+
+ = esc($data[$year]) ?>
+
+
+
+ ';
+ } else {
+ $colorClass = 'text-success fw-bold';
+ $symbol = ' ';
+ }
+ ?>
+ = $symbol . esc($percent) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/spesific_command_index.php b/app/Views/spesific_command_index.php
new file mode 100644
index 0000000..d6ed4ad
--- /dev/null
+++ b/app/Views/spesific_command_index.php
@@ -0,0 +1,41 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+
Under Maintenance
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/techinst_index.php b/app/Views/techinst_index.php
new file mode 100644
index 0000000..4a5cefc
--- /dev/null
+++ b/app/Views/techinst_index.php
@@ -0,0 +1,59 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Tech. List For Instrument =$instname;?>
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+ ID Tech. Text Tech. Desc.
+
+
+
+
+
+ =$techid;?> =$techtext;?> =$techdesc;?>
+
+ edit
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/techs_editor.php b/app/Views/techs_editor.php
new file mode 100644
index 0000000..b983cde
--- /dev/null
+++ b/app/Views/techs_editor.php
@@ -0,0 +1,111 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Technical Notes Editor
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
Technical Notes Editor
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
diff --git a/app/Views/techs_index.php b/app/Views/techs_index.php
new file mode 100644
index 0000000..4bb2f92
--- /dev/null
+++ b/app/Views/techs_index.php
@@ -0,0 +1,57 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Technical Notes List
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+
+
+
+
+ ID Tech. Text Tech. Desc.
+
+
+
+
+
+ =$techid;?> =$techtext;?> =$techdesc;?>
+
+ edit
+
+
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/Views/tests_editor.php b/app/Views/tests_editor.php
new file mode 100644
index 0000000..8104719
--- /dev/null
+++ b/app/Views/tests_editor.php
@@ -0,0 +1,87 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Test Editor
+
+
+
+
+ = $validation->listErrors(); ?>
+
+
+
+
+
+
+= $this->endSection() ?>
+
+= $this->section('script') ?>
+
+= $this->endSection() ?>
diff --git a/app/Views/tests_index.php b/app/Views/tests_index.php
new file mode 100644
index 0000000..b8d06a4
--- /dev/null
+++ b/app/Views/tests_index.php
@@ -0,0 +1,40 @@
+= $this->extend('layouts/main.php') ?>
+
+= $this->section('content') ?>
+
+
+
+
Dictionary Test
+
+
+
+
+
+
+
+
+
+
+ Testcode Testname
+
+
+
+
+ =$testcode;?> =$testname;?> edit
+
+
+
+
+
+
+= $this->endSection() ?>
\ No newline at end of file
diff --git a/app/index.html b/app/index.html
new file mode 100644
index 0000000..69df4e1
--- /dev/null
+++ b/app/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/builds b/builds
new file mode 100644
index 0000000..cc2ca08
--- /dev/null
+++ b/builds
@@ -0,0 +1,125 @@
+#!/usr/bin/env php
+ 'vcs',
+ 'url' => GITHUB_URL,
+ ];
+ }
+
+ $array['require']['codeigniter4/codeigniter4'] = 'dev-develop';
+ unset($array['require']['codeigniter4/framework']);
+ } else {
+ unset($array['minimum-stability']);
+
+ if (isset($array['repositories'])) {
+ foreach ($array['repositories'] as $i => $repository) {
+ if ($repository['url'] === GITHUB_URL) {
+ unset($array['repositories'][$i]);
+ break;
+ }
+ }
+
+ if (empty($array['repositories'])) {
+ unset($array['repositories']);
+ }
+ }
+
+ $array['require']['codeigniter4/framework'] = LATEST_RELEASE;
+ unset($array['require']['codeigniter4/codeigniter4']);
+ }
+
+ file_put_contents($file, json_encode($array, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);
+
+ $modified[] = $file;
+ } else {
+ echo 'Warning: Unable to decode composer.json! Skipping...' . PHP_EOL;
+ }
+ } else {
+ echo 'Warning: Unable to read composer.json! Skipping...' . PHP_EOL;
+ }
+}
+
+$files = [
+ __DIR__ . DIRECTORY_SEPARATOR . 'app/Config/Paths.php',
+ __DIR__ . DIRECTORY_SEPARATOR . 'phpunit.xml.dist',
+ __DIR__ . DIRECTORY_SEPARATOR . 'phpunit.xml',
+];
+
+foreach ($files as $file) {
+ if (is_file($file)) {
+ $contents = file_get_contents($file);
+
+ if ($dev) {
+ $contents = str_replace('vendor/codeigniter4/framework', 'vendor/codeigniter4/codeigniter4', $contents);
+ } else {
+ $contents = str_replace('vendor/codeigniter4/codeigniter4', 'vendor/codeigniter4/framework', $contents);
+ }
+
+ file_put_contents($file, $contents);
+
+ $modified[] = $file;
+ }
+}
+
+if ($modified === []) {
+ echo 'No files modified.' . PHP_EOL;
+} else {
+ echo 'The following files were modified:' . PHP_EOL;
+
+ foreach ($modified as $file) {
+ echo " * {$file}" . PHP_EOL;
+ }
+
+ echo 'Run `composer update` to sync changes with your vendor folder.' . PHP_EOL;
+}
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..38a51e2
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,43 @@
+{
+ "name": "codeigniter4/appstarter",
+ "description": "CodeIgniter4 starter app",
+ "license": "MIT",
+ "type": "project",
+ "homepage": "https://codeigniter.com",
+ "support": {
+ "forum": "https://forum.codeigniter.com/",
+ "source": "https://github.com/codeigniter4/CodeIgniter4",
+ "slack": "https://codeigniterchat.slack.com"
+ },
+ "require": {
+ "php": "^8.1",
+ "codeigniter4/framework": "^4.0"
+ },
+ "require-dev": {
+ "fakerphp/faker": "^1.9",
+ "mikey179/vfsstream": "^1.6",
+ "phpunit/phpunit": "^10.5.16"
+ },
+ "autoload": {
+ "psr-4": {
+ "App\\": "app/",
+ "Config\\": "app/Config/"
+ },
+ "exclude-from-classmap": [
+ "**/Database/Migrations/**"
+ ]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Tests\\Support\\": "tests/_support"
+ }
+ },
+ "config": {
+ "optimize-autoloader": true,
+ "preferred-install": "dist",
+ "sort-packages": true
+ },
+ "scripts": {
+ "test": "phpunit"
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..e6cd003
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,2087 @@
+{
+ "_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": "657ac079313fab046eb8c29fe1d6992b",
+ "packages": [
+ {
+ "name": "codeigniter4/framework",
+ "version": "v4.6.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/codeigniter4/framework.git",
+ "reference": "68d1a5896106f869452dd369a690dd5bc75160fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/codeigniter4/framework/zipball/68d1a5896106f869452dd369a690dd5bc75160fb",
+ "reference": "68d1a5896106f869452dd369a690dd5bc75160fb",
+ "shasum": ""
+ },
+ "require": {
+ "ext-intl": "*",
+ "ext-mbstring": "*",
+ "laminas/laminas-escaper": "^2.17",
+ "php": "^8.1",
+ "psr/log": "^3.0"
+ },
+ "require-dev": {
+ "codeigniter/coding-standard": "^1.7",
+ "fakerphp/faker": "^1.24",
+ "friendsofphp/php-cs-fixer": "^3.47.1",
+ "kint-php/kint": "^6.0",
+ "mikey179/vfsstream": "^1.6.12",
+ "nexusphp/cs-config": "^3.6",
+ "phpunit/phpunit": "^10.5.16 || ^11.2",
+ "predis/predis": "^3.0"
+ },
+ "suggest": {
+ "ext-curl": "If you use CURLRequest class",
+ "ext-dom": "If you use TestResponse",
+ "ext-exif": "If you run Image class tests",
+ "ext-fileinfo": "Improves mime type detection for files",
+ "ext-gd": "If you use Image class GDHandler",
+ "ext-imagick": "If you use Image class ImageMagickHandler",
+ "ext-libxml": "If you use TestResponse",
+ "ext-memcache": "If you use Cache class MemcachedHandler with Memcache",
+ "ext-memcached": "If you use Cache class MemcachedHandler with Memcached",
+ "ext-mysqli": "If you use MySQL",
+ "ext-oci8": "If you use Oracle Database",
+ "ext-pgsql": "If you use PostgreSQL",
+ "ext-readline": "Improves CLI::input() usability",
+ "ext-redis": "If you use Cache class RedisHandler",
+ "ext-simplexml": "If you format XML",
+ "ext-sodium": "If you use Encryption SodiumHandler",
+ "ext-sqlite3": "If you use SQLite3",
+ "ext-sqlsrv": "If you use SQL Server",
+ "ext-xdebug": "If you use CIUnitTestCase::assertHeaderEmitted()"
+ },
+ "type": "project",
+ "autoload": {
+ "psr-4": {
+ "CodeIgniter\\": "system/"
+ },
+ "exclude-from-classmap": [
+ "**/Database/Migrations/**"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The CodeIgniter framework v4",
+ "homepage": "https://codeigniter.com",
+ "support": {
+ "forum": "https://forum.codeigniter.com/",
+ "slack": "https://codeigniterchat.slack.com",
+ "source": "https://github.com/codeigniter4/CodeIgniter4"
+ },
+ "time": "2025-08-02T13:36:13+00:00"
+ },
+ {
+ "name": "laminas/laminas-escaper",
+ "version": "2.17.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laminas/laminas-escaper.git",
+ "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba",
+ "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-mbstring": "*",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
+ },
+ "conflict": {
+ "zendframework/zend-escaper": "*"
+ },
+ "require-dev": {
+ "infection/infection": "^0.29.8",
+ "laminas/laminas-coding-standard": "~3.0.1",
+ "phpunit/phpunit": "^10.5.45",
+ "psalm/plugin-phpunit": "^0.19.2",
+ "vimeo/psalm": "^6.6.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Laminas\\Escaper\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs",
+ "homepage": "https://laminas.dev",
+ "keywords": [
+ "escaper",
+ "laminas"
+ ],
+ "support": {
+ "chat": "https://laminas.dev/chat",
+ "docs": "https://docs.laminas.dev/laminas-escaper/",
+ "forum": "https://discourse.laminas.dev",
+ "issues": "https://github.com/laminas/laminas-escaper/issues",
+ "rss": "https://github.com/laminas/laminas-escaper/releases.atom",
+ "source": "https://github.com/laminas/laminas-escaper"
+ },
+ "funding": [
+ {
+ "url": "https://funding.communitybridge.org/projects/laminas-project",
+ "type": "community_bridge"
+ }
+ ],
+ "time": "2025-05-06T19:29:36+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.2"
+ },
+ "time": "2024-09-11T13:17:53+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "fakerphp/faker",
+ "version": "v1.24.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FakerPHP/Faker.git",
+ "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+ "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "psr/container": "^1.0 || ^2.0",
+ "symfony/deprecation-contracts": "^2.2 || ^3.0"
+ },
+ "conflict": {
+ "fzaninotto/faker": "*"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "doctrine/persistence": "^1.3 || ^2.0",
+ "ext-intl": "*",
+ "phpunit/phpunit": "^9.5.26",
+ "symfony/phpunit-bridge": "^5.4.16"
+ },
+ "suggest": {
+ "doctrine/orm": "Required to use Faker\\ORM\\Doctrine",
+ "ext-curl": "Required by Faker\\Provider\\Image to download images.",
+ "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.",
+ "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.",
+ "ext-mbstring": "Required for multibyte Unicode string functionality."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Faker\\": "src/Faker/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "François Zaninotto"
+ }
+ ],
+ "description": "Faker is a PHP library that generates fake data for you.",
+ "keywords": [
+ "data",
+ "faker",
+ "fixtures"
+ ],
+ "support": {
+ "issues": "https://github.com/FakerPHP/Faker/issues",
+ "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1"
+ },
+ "time": "2024-11-21T13:46:39+00:00"
+ },
+ {
+ "name": "mikey179/vfsstream",
+ "version": "v1.6.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bovigo/vfsStream.git",
+ "reference": "fe695ec993e0a55c3abdda10a9364eb31c6f1bf0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/fe695ec993e0a55c3abdda10a9364eb31c6f1bf0",
+ "reference": "fe695ec993e0a55c3abdda10a9364eb31c6f1bf0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5||^8.5||^9.6",
+ "yoast/phpunit-polyfills": "^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "org\\bovigo\\vfs\\": "src/main/php"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Frank Kleine",
+ "homepage": "http://frankkleine.de/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Virtual file system to mock the real file system in unit tests.",
+ "homepage": "http://vfs.bovigo.org/",
+ "support": {
+ "issues": "https://github.com/bovigo/vfsStream/issues",
+ "source": "https://github.com/bovigo/vfsStream/tree/master",
+ "wiki": "https://github.com/bovigo/vfsStream/wiki"
+ },
+ "time": "2024-08-29T18:43:31+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-01T08:46:24+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
+ "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1"
+ },
+ "time": "2025-08-13T20:13:15+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "10.1.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "7e308268858ed6baedc8704a304727d20bc07c77"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77",
+ "reference": "7e308268858ed6baedc8704a304727d20bc07c77",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.19.1 || ^5.1.0",
+ "php": ">=8.1",
+ "phpunit/php-file-iterator": "^4.1.0",
+ "phpunit/php-text-template": "^3.0.1",
+ "sebastian/code-unit-reverse-lookup": "^3.0.0",
+ "sebastian/complexity": "^3.2.0",
+ "sebastian/environment": "^6.1.0",
+ "sebastian/lines-of-code": "^2.0.2",
+ "sebastian/version": "^4.0.1",
+ "theseer/tokenizer": "^1.2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.1"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "10.1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-22T04:31:57+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "4.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T06:24:48+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:56:09+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-08-31T14:07:24+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:57:52+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "10.5.51",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "ace160e31aaa317a99c411410c40c502b4be42a4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ace160e31aaa317a99c411410c40c502b4be42a4",
+ "reference": "ace160e31aaa317a99c411410c40c502b4be42a4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.4",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.1",
+ "phpunit/php-code-coverage": "^10.1.16",
+ "phpunit/php-file-iterator": "^4.1.0",
+ "phpunit/php-invoker": "^4.0.0",
+ "phpunit/php-text-template": "^3.0.1",
+ "phpunit/php-timer": "^6.0.0",
+ "sebastian/cli-parser": "^2.0.1",
+ "sebastian/code-unit": "^2.0.0",
+ "sebastian/comparator": "^5.0.3",
+ "sebastian/diff": "^5.1.1",
+ "sebastian/environment": "^6.1.0",
+ "sebastian/exporter": "^5.1.2",
+ "sebastian/global-state": "^6.0.2",
+ "sebastian/object-enumerator": "^5.0.0",
+ "sebastian/recursion-context": "^5.0.1",
+ "sebastian/type": "^4.0.0",
+ "sebastian/version": "^4.0.1"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "10.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.51"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-12T07:31:25+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:12:49+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:58:43+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T06:59:15+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "5.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
+ "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/diff": "^5.0",
+ "sebastian/exporter": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-18T14:56:07+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "68ff824baeae169ec9f2137158ee529584553799"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
+ "reference": "68ff824baeae169ec9f2137158ee529584553799",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-21T08:37:17+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "5.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0",
+ "symfony/process": "^6.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:15:17+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "6.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-23T08:47:14+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "5.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "955288482d97c19a372d3f31006ab3f37da47adf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
+ "reference": "955288482d97c19a372d3f31006ab3f37da47adf",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:17:12+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:19:19+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-12-21T08:38:20+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:08:32+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:06:18+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "5.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a",
+ "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-10T07:50:56+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-03T07:10:45+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2023-02-07T11:34:05+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:36:25+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "^8.1"
+ },
+ "platform-dev": {},
+ "plugin-api-version": "2.6.0"
+}
diff --git a/env b/env
new file mode 100644
index 0000000..f359ec2
--- /dev/null
+++ b/env
@@ -0,0 +1,69 @@
+#--------------------------------------------------------------------
+# Example Environment Configuration file
+#
+# This file can be used as a starting point for your own
+# custom .env files, and contains most of the possible settings
+# available in a default install.
+#
+# By default, all of the settings are commented out. If you want
+# to override the setting, you must un-comment it by removing the '#'
+# at the beginning of the line.
+#--------------------------------------------------------------------
+
+#--------------------------------------------------------------------
+# ENVIRONMENT
+#--------------------------------------------------------------------
+
+# CI_ENVIRONMENT = production
+
+#--------------------------------------------------------------------
+# APP
+#--------------------------------------------------------------------
+
+# app.baseURL = ''
+# If you have trouble with `.`, you could also use `_`.
+# app_baseURL = ''
+# app.forceGlobalSecureRequests = false
+# app.CSPEnabled = false
+
+#--------------------------------------------------------------------
+# DATABASE
+#--------------------------------------------------------------------
+
+# database.default.hostname = localhost
+# database.default.database = ci4
+# database.default.username = root
+# database.default.password = root
+# database.default.DBDriver = MySQLi
+# database.default.DBPrefix =
+# database.default.port = 3306
+
+# If you use MySQLi as tests, first update the values of Config\Database::$tests.
+# database.tests.hostname = localhost
+# database.tests.database = ci4_test
+# database.tests.username = root
+# database.tests.password = root
+# database.tests.DBDriver = MySQLi
+# database.tests.DBPrefix =
+# database.tests.charset = utf8mb4
+# database.tests.DBCollat = utf8mb4_general_ci
+# database.tests.port = 3306
+
+#--------------------------------------------------------------------
+# ENCRYPTION
+#--------------------------------------------------------------------
+
+# encryption.key =
+
+#--------------------------------------------------------------------
+# SESSION
+#--------------------------------------------------------------------
+
+# session.driver = 'CodeIgniter\Session\Handlers\FileHandler'
+# session.savePath = null
+
+#--------------------------------------------------------------------
+# LOGGER
+#--------------------------------------------------------------------
+
+# logger.threshold = 4
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..b408a99
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ ./tests
+
+
+
+
+
+
+
+
+
+ ./app
+
+
+ ./app/Views
+ ./app/Config/Routes.php
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/preload.php b/preload.php
new file mode 100644
index 0000000..9d16bb3
--- /dev/null
+++ b/preload.php
@@ -0,0 +1,110 @@
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+use CodeIgniter\Boot;
+use Config\Paths;
+
+/*
+ *---------------------------------------------------------------
+ * Sample file for Preloading
+ *---------------------------------------------------------------
+ * See https://www.php.net/manual/en/opcache.preloading.php
+ *
+ * How to Use:
+ * 0. Copy this file to your project root folder.
+ * 1. Set the $paths property of the preload class below.
+ * 2. Set opcache.preload in php.ini.
+ * php.ini:
+ * opcache.preload=/path/to/preload.php
+ */
+
+// Load the paths config file
+require __DIR__ . '/app/Config/Paths.php';
+
+// Path to the front controller
+define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR);
+
+class preload
+{
+ /**
+ * @var array Paths to preload.
+ */
+ private array $paths = [
+ [
+ 'include' => __DIR__ . '/vendor/codeigniter4/framework/system', // Change this path if using manual installation
+ 'exclude' => [
+ // Not needed if you don't use them.
+ '/system/Database/OCI8/',
+ '/system/Database/Postgre/',
+ '/system/Database/SQLite3/',
+ '/system/Database/SQLSRV/',
+ // Not needed for web apps.
+ '/system/Database/Seeder.php',
+ '/system/Test/',
+ '/system/CLI/',
+ '/system/Commands/',
+ '/system/Publisher/',
+ '/system/ComposerScripts.php',
+ // Not Class/Function files.
+ '/system/Config/Routes.php',
+ '/system/Language/',
+ '/system/bootstrap.php',
+ '/system/util_bootstrap.php',
+ '/system/rewrite.php',
+ '/Views/',
+ // Errors occur.
+ '/system/ThirdParty/',
+ ],
+ ],
+ ];
+
+ public function __construct()
+ {
+ $this->loadAutoloader();
+ }
+
+ private function loadAutoloader(): void
+ {
+ $paths = new Paths();
+ require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'Boot.php';
+
+ Boot::preload($paths);
+ }
+
+ /**
+ * Load PHP files.
+ */
+ public function load(): void
+ {
+ foreach ($this->paths as $path) {
+ $directory = new RecursiveDirectoryIterator($path['include']);
+ $fullTree = new RecursiveIteratorIterator($directory);
+ $phpFiles = new RegexIterator(
+ $fullTree,
+ '/.+((? $file) {
+ foreach ($path['exclude'] as $exclude) {
+ if (str_contains($file[0], $exclude)) {
+ continue 2;
+ }
+ }
+
+ require_once $file[0];
+ echo 'Loaded: ' . $file[0] . "\n";
+ }
+ }
+ }
+}
+
+(new preload())->load();
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..abac3cb
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,49 @@
+# Disable directory browsing
+Options -Indexes
+
+# ----------------------------------------------------------------------
+# Rewrite engine
+# ----------------------------------------------------------------------
+
+# Turning on the rewrite engine is necessary for the following rules and features.
+# FollowSymLinks must be enabled for this to work.
+
+ Options +FollowSymlinks
+ RewriteEngine On
+
+ # If you installed CodeIgniter in a subfolder, you will need to
+ # change the following line to match the subfolder you need.
+ # http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase
+ # RewriteBase /
+
+ # Redirect Trailing Slashes...
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_URI} (.+)/$
+ RewriteRule ^ %1 [L,R=301]
+
+ # Rewrite "www.example.com -> example.com"
+ RewriteCond %{HTTPS} !=on
+ RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+ RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
+
+ # Checks to see if the user is attempting to access a valid file,
+ # such as an image or css document, if this isn't true it sends the
+ # request to the front controller, index.php
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^([\s\S]*)$ index.php/$1 [L,NC,QSA]
+
+ # Ensure Authorization header is passed along
+ RewriteCond %{HTTP:Authorization} .
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+
+
+
+ # If we don't have mod_rewrite installed, all 404's
+ # can be sent to index.php, and everything works as normal.
+ ErrorDocument 404 index.php
+
+
+# Disable server signature start
+ServerSignature Off
+# Disable server signature end
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..7ecfce2
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/index.php b/public/index.php
new file mode 100644
index 0000000..a0a20db
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,59 @@
+systemDirectory . '/Boot.php';
+
+exit(Boot::bootWeb($paths));
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..9e60f97
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:
diff --git a/spark b/spark
new file mode 100644
index 0000000..e7871ea
--- /dev/null
+++ b/spark
@@ -0,0 +1,87 @@
+#!/usr/bin/env php
+
+ *
+ * For the full copyright and license information, please view
+ * the LICENSE file that was distributed with this source code.
+ */
+
+use CodeIgniter\Boot;
+use Config\Paths;
+
+/*
+ * --------------------------------------------------------------------
+ * CODEIGNITER COMMAND-LINE TOOLS
+ * --------------------------------------------------------------------
+ * The main entry point into the CLI system and allows you to run
+ * commands and perform maintenance on your application.
+ */
+
+/*
+ *---------------------------------------------------------------
+ * CHECK SERVER API
+ *---------------------------------------------------------------
+ */
+
+// Refuse to run when called from php-cgi
+if (str_starts_with(PHP_SAPI, 'cgi')) {
+ exit("The cli tool is not supported when running php-cgi. It needs php-cli to function!\n\n");
+}
+
+/*
+ *---------------------------------------------------------------
+ * CHECK PHP VERSION
+ *---------------------------------------------------------------
+ */
+
+$minPhpVersion = '8.1'; // If you update this, don't forget to update `public/index.php`.
+if (version_compare(PHP_VERSION, $minPhpVersion, '<')) {
+ $message = sprintf(
+ 'Your PHP version must be %s or higher to run CodeIgniter. Current version: %s',
+ $minPhpVersion,
+ PHP_VERSION,
+ );
+
+ exit($message);
+}
+
+// We want errors to be shown when using it from the CLI.
+error_reporting(E_ALL);
+ini_set('display_errors', '1');
+
+/*
+ *---------------------------------------------------------------
+ * SET THE CURRENT DIRECTORY
+ *---------------------------------------------------------------
+ */
+
+// Path to the front controller
+define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR);
+
+// Ensure the current directory is pointing to the front controller's directory
+chdir(FCPATH);
+
+/*
+ *---------------------------------------------------------------
+ * BOOTSTRAP THE APPLICATION
+ *---------------------------------------------------------------
+ * This process sets up the path constants, loads and registers
+ * our autoloader, along with Composer's, loads our constants
+ * and fires up an environment-specific bootstrapping.
+ */
+
+// LOAD OUR PATHS CONFIG FILE
+// This is the line that might need to be changed, depending on your folder structure.
+require FCPATH . '../app/Config/Paths.php';
+// ^^^ Change this line if you move your application folder
+
+$paths = new Paths();
+
+// LOAD THE FRAMEWORK BOOTSTRAP FILE
+require $paths->systemDirectory . '/Boot.php';
+
+exit(Boot::bootSpark($paths));
diff --git a/tests/.htaccess b/tests/.htaccess
new file mode 100644
index 0000000..3462048
--- /dev/null
+++ b/tests/.htaccess
@@ -0,0 +1,6 @@
+
+ Require all denied
+
+
+ Deny from all
+
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..fc40e44
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,118 @@
+# Running Application Tests
+
+This is the quick-start to CodeIgniter testing. Its intent is to describe what
+it takes to set up your application and get it ready to run unit tests.
+It is not intended to be a full description of the test features that you can
+use to test your application. Those details can be found in the documentation.
+
+## Resources
+
+* [CodeIgniter 4 User Guide on Testing](https://codeigniter.com/user_guide/testing/index.html)
+* [PHPUnit docs](https://phpunit.de/documentation.html)
+* [Any tutorials on Unit testing in CI4?](https://forum.codeigniter.com/showthread.php?tid=81830)
+
+## Requirements
+
+It is recommended to use the latest version of PHPUnit. At the time of this
+writing, we are running version 9.x. Support for this has been built into the
+**composer.json** file that ships with CodeIgniter and can easily be installed
+via [Composer](https://getcomposer.org/) if you don't already have it installed globally.
+
+```console
+> composer install
+```
+
+If running under macOS or Linux, you can create a symbolic link to make running tests a touch nicer.
+
+```console
+> ln -s ./vendor/bin/phpunit ./phpunit
+```
+
+You also need to install [XDebug](https://xdebug.org/docs/install) in order
+for code coverage to be calculated successfully. After installing `XDebug`, you must add `xdebug.mode=coverage` in the **php.ini** file to enable code coverage.
+
+## Setting Up
+
+A number of the tests use a running database.
+In order to set up the database edit the details for the `tests` group in
+**app/Config/Database.php** or **.env**.
+Make sure that you provide a database engine that is currently running on your machine.
+More details on a test database setup are in the
+[Testing Your Database](https://codeigniter.com/user_guide/testing/database.html) section of the documentation.
+
+## Running the tests
+
+The entire test suite can be run by simply typing one command-line command from the main directory.
+
+```console
+> ./phpunit
+```
+
+If you are using Windows, use the following command.
+
+```console
+> vendor\bin\phpunit
+```
+
+You can limit tests to those within a single test directory by specifying the
+directory name after phpunit.
+
+```console
+> ./phpunit app/Models
+```
+
+## Generating Code Coverage
+
+To generate coverage information, including HTML reports you can view in your browser,
+you can use the following command:
+
+```console
+> ./phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m
+```
+
+This runs all of the tests again collecting information about how many lines,
+functions, and files are tested. It also reports the percentage of the code that is covered by tests.
+It is collected in two formats: a simple text file that provides an overview as well
+as a comprehensive collection of HTML files that show the status of every line of code in the project.
+
+The text file can be found at **tests/coverage.txt**.
+The HTML files can be viewed by opening **tests/coverage/index.html** in your favorite browser.
+
+## PHPUnit XML Configuration
+
+The repository has a ``phpunit.xml.dist`` file in the project root that's used for
+PHPUnit configuration. This is used to provide a default configuration if you
+do not have your own configuration file in the project root.
+
+The normal practice would be to copy ``phpunit.xml.dist`` to ``phpunit.xml``
+(which is git ignored), and to tailor it as you see fit.
+For instance, you might wish to exclude database tests, or automatically generate
+HTML code coverage reports.
+
+## Test Cases
+
+Every test needs a *test case*, or class that your tests extend. CodeIgniter 4
+provides one class that you may use directly:
+* `CodeIgniter\Test\CIUnitTestCase`
+
+Most of the time you will want to write your own test cases that extend `CIUnitTestCase`
+to hold functions and services common to your test suites.
+
+## Creating Tests
+
+All tests go in the **tests/** directory. Each test file is a class that extends a
+**Test Case** (see above) and contains methods for the individual tests. These method
+names must start with the word "test" and should have descriptive names for precisely what
+they are testing:
+`testUserCanModifyFile()` `testOutputColorMatchesInput()` `testIsLoggedInFailsWithInvalidUser()`
+
+Writing tests is an art, and there are many resources available to help learn how.
+Review the links above and always pay attention to your code coverage.
+
+### Database Tests
+
+Tests can include migrating, seeding, and testing against a mock or live database.
+Be sure to modify the test case (or create your own) to point to your seed and migrations
+and include any additional steps to be run before tests in the `setUp()` method.
+See [Testing Your Database](https://codeigniter.com/user_guide/testing/database.html)
+for details.
diff --git a/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php b/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php
new file mode 100644
index 0000000..a73356d
--- /dev/null
+++ b/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php
@@ -0,0 +1,37 @@
+forge->addField('id');
+ $this->forge->addField([
+ 'name' => ['type' => 'varchar', 'constraint' => 31],
+ 'uid' => ['type' => 'varchar', 'constraint' => 31],
+ 'class' => ['type' => 'varchar', 'constraint' => 63],
+ 'icon' => ['type' => 'varchar', 'constraint' => 31],
+ 'summary' => ['type' => 'varchar', 'constraint' => 255],
+ 'created_at' => ['type' => 'datetime', 'null' => true],
+ 'updated_at' => ['type' => 'datetime', 'null' => true],
+ 'deleted_at' => ['type' => 'datetime', 'null' => true],
+ ]);
+
+ $this->forge->addKey('name');
+ $this->forge->addKey('uid');
+ $this->forge->addKey(['deleted_at', 'id']);
+ $this->forge->addKey('created_at');
+
+ $this->forge->createTable('factories');
+ }
+
+ public function down(): void
+ {
+ $this->forge->dropTable('factories');
+ }
+}
diff --git a/tests/_support/Database/Seeds/ExampleSeeder.php b/tests/_support/Database/Seeds/ExampleSeeder.php
new file mode 100644
index 0000000..619fc27
--- /dev/null
+++ b/tests/_support/Database/Seeds/ExampleSeeder.php
@@ -0,0 +1,41 @@
+ 'Test Factory',
+ 'uid' => 'test001',
+ 'class' => 'Factories\Tests\NewFactory',
+ 'icon' => 'fas fa-puzzle-piece',
+ 'summary' => 'Longer sample text for testing',
+ ],
+ [
+ 'name' => 'Widget Factory',
+ 'uid' => 'widget',
+ 'class' => 'Factories\Tests\WidgetPlant',
+ 'icon' => 'fas fa-puzzle-piece',
+ 'summary' => 'Create widgets in your factory',
+ ],
+ [
+ 'name' => 'Evil Factory',
+ 'uid' => 'evil-maker',
+ 'class' => 'Factories\Evil\MyFactory',
+ 'icon' => 'fas fa-book-dead',
+ 'summary' => 'Abandon all hope, ye who enter here',
+ ],
+ ];
+
+ $builder = $this->db->table('factories');
+
+ foreach ($factories as $factory) {
+ $builder->insert($factory);
+ }
+ }
+}
diff --git a/tests/_support/Libraries/ConfigReader.php b/tests/_support/Libraries/ConfigReader.php
new file mode 100644
index 0000000..0bb4a8f
--- /dev/null
+++ b/tests/_support/Libraries/ConfigReader.php
@@ -0,0 +1,19 @@
+findAll();
+
+ // Make sure the count is as expected
+ $this->assertCount(3, $objects);
+ }
+
+ public function testSoftDeleteLeavesRow(): void
+ {
+ $model = new ExampleModel();
+ $this->setPrivateProperty($model, 'useSoftDeletes', true);
+ $this->setPrivateProperty($model, 'tempUseSoftDeletes', true);
+
+ /** @var stdClass $object */
+ $object = $model->first();
+ $model->delete($object->id);
+
+ // The model should no longer find it
+ $this->assertNull($model->find($object->id));
+
+ // ... but it should still be in the database
+ $result = $model->builder()->where('id', $object->id)->get()->getResult();
+
+ $this->assertCount(1, $result);
+ }
+}
diff --git a/tests/index.html b/tests/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/tests/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/tests/session/ExampleSessionTest.php b/tests/session/ExampleSessionTest.php
new file mode 100644
index 0000000..33242a4
--- /dev/null
+++ b/tests/session/ExampleSessionTest.php
@@ -0,0 +1,17 @@
+set('logged_in', 123);
+ $this->assertSame(123, $session->get('logged_in'));
+ }
+}
diff --git a/tests/unit/HealthTest.php b/tests/unit/HealthTest.php
new file mode 100644
index 0000000..b3e480f
--- /dev/null
+++ b/tests/unit/HealthTest.php
@@ -0,0 +1,49 @@
+assertTrue(defined('APPPATH'));
+ }
+
+ public function testBaseUrlHasBeenSet(): void
+ {
+ $validation = service('validation');
+
+ $env = false;
+
+ // Check the baseURL in .env
+ if (is_file(HOMEPATH . '.env')) {
+ $env = preg_grep('/^app\.baseURL = ./', file(HOMEPATH . '.env')) !== false;
+ }
+
+ if ($env) {
+ // BaseURL in .env is a valid URL?
+ // phpunit.xml.dist sets app.baseURL in $_SERVER
+ // So if you set app.baseURL in .env, it takes precedence
+ $config = new App();
+ $this->assertTrue(
+ $validation->check($config->baseURL, 'valid_url'),
+ 'baseURL "' . $config->baseURL . '" in .env is not valid URL',
+ );
+ }
+
+ // Get the baseURL in app/Config/App.php
+ // You can't use Config\App, because phpunit.xml.dist sets app.baseURL
+ $reader = new ConfigReader();
+
+ // BaseURL in app/Config/App.php is a valid URL?
+ $this->assertTrue(
+ $validation->check($reader->baseURL, 'valid_url'),
+ 'baseURL "' . $reader->baseURL . '" in app/Config/App.php is not valid URL',
+ );
+ }
+}
diff --git a/writable/.htaccess b/writable/.htaccess
new file mode 100644
index 0000000..3462048
--- /dev/null
+++ b/writable/.htaccess
@@ -0,0 +1,6 @@
+
+ Require all denied
+
+
+ Deny from all
+
diff --git a/writable/cache/index.html b/writable/cache/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/writable/cache/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/writable/debugbar/index.html b/writable/debugbar/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/writable/debugbar/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/writable/index.html b/writable/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/writable/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/writable/logs/index.html b/writable/logs/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/writable/logs/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/writable/session/index.html b/writable/session/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/writable/session/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+
diff --git a/writable/uploads/index.html b/writable/uploads/index.html
new file mode 100644
index 0000000..b702fbc
--- /dev/null
+++ b/writable/uploads/index.html
@@ -0,0 +1,11 @@
+
+
+
+ 403 Forbidden
+
+
+
+Directory access is forbidden.
+
+
+