├── .gitignore
├── Resources
├── js
│ └── scripts.js
└── css
│ └── styles.css
├── Src
├── Configs
│ ├── app.php
│ └── database.php
├── Exception
│ ├── BadRequestException.php
│ ├── NotFoundException.php
│ ├── DatabaseConnectionException.php
│ ├── InvalidArgumentException.php
│ ├── InvalidLogLevelArgument.php
│ ├── MissingArgumentException.php
│ ├── exception.php
│ ├── ExceptionHandler.php
│ └── BaseException.php
├── Contracts
│ ├── DatabaseConnectionInterface.php
│ ├── RepositoryInterface.php
│ └── LoggerInterface.php
├── Entity
│ ├── Entity.php
│ └── BugReport.php
├── Repository
│ ├── BugReportRepository.php
│ └── Repository.php
├── read.php
├── Logger
│ ├── LogLevel.php
│ └── Logger.php
├── Helpers
│ ├── Config.php
│ ├── HttpClient.php
│ ├── DbQueryBuilderFactory.php
│ └── App.php
├── Database
│ ├── AbstractConnection.php
│ ├── PDOQueryBuilder.php
│ ├── Query.php
│ ├── MySQLiConnection.php
│ ├── PDOConnection.php
│ ├── MySQLiQueryBuilder.php
│ └── QueryBuilder.php
├── delete.php
├── add.php
└── update.php
├── header.php
├── composer.json
├── phpunit.xml
├── Tests
├── Functional
│ ├── HomepageTest.php
│ └── CrudTest.php
└── Units
│ ├── ApplicationTest.php
│ ├── LoggerTest.php
│ ├── DatabaseConnectionTest.php
│ ├── RepositoryTest.php
│ └── QueryBuilderTest.php
├── LICENSE.md
├── README.md
├── addModal.php
├── store.sql
├── index.php
└── composer.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor/
3 | /Src/Logger/*.log
4 |
--------------------------------------------------------------------------------
/Resources/js/scripts.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function(){
2 | // Activate tooltip
3 | $('[data-toggle="tooltip"]').tooltip();
4 | });
--------------------------------------------------------------------------------
/Src/Configs/app.php:
--------------------------------------------------------------------------------
1 | 'Bug Report App',
6 | 'env' => 'local',
7 | 'debug' => true,
8 | 'log_path' => __DIR__ . '/../Logger',
9 | ];
--------------------------------------------------------------------------------
/Src/Exception/BadRequestException.php:
--------------------------------------------------------------------------------
1 | findAll();
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "terdia/bug-report-app",
3 | "require": {},
4 | "require-dev": {
5 | "phpunit/phpunit": "^7"
6 | },
7 | "autoload": {
8 | "psr-4": {
9 | "App\\":"Src"
10 | }
11 | },
12 | "autoload-dev": {
13 | "psr-4": {
14 | "Tests\\": "Tests"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Src/Logger/LogLevel.php:
--------------------------------------------------------------------------------
1 | [
6 | 'driver' => 'mysql',
7 | 'host' => 'localhost',
8 | 'db_name' => 'bug_app',
9 | 'db_username' => 'root',
10 | 'db_user_password' => '',
11 | 'default_fetch' => PDO::FETCH_OBJ,
12 | ],
13 | 'mysqli' => [
14 | 'host' => 'localhost',
15 | 'db_name' => 'bug_app',
16 | 'db_username' => 'root',
17 | 'db_user_password' => '',
18 | 'default_fetch' => MYSQLI_ASSOC,
19 | ],
20 | ];
--------------------------------------------------------------------------------
/Src/Contracts/RepositoryInterface.php:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 | Tests/Unit
13 |
14 |
15 |
16 | Tests/Functional
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Src/Exception/ExceptionHandler.php:
--------------------------------------------------------------------------------
1 | isDebugMode()){
17 | var_dump($exception);
18 | }else{
19 | echo "This should not have happened, please try again";
20 | }
21 | exit;
22 | }
23 |
24 | public function convertWarningsAndNoticesToException($severity, $message, $file, $line)
25 | {
26 | throw new ErrorException($message, $severity, $severity, $file, $line);
27 | }
28 | }
--------------------------------------------------------------------------------
/Tests/Functional/HomepageTest.php:
--------------------------------------------------------------------------------
1 | get('http://localhost/bug-report-app/index.php');
16 | $response = json_decode($response, true);
17 | self::assertEquals(200, $response['statusCode']);
18 | self::assertStringContainsString('Bug Report App', $response['content']);
19 | self::assertStringContainsString('
Manage Bug Reports
', $response['content']);
20 | }
21 | }
--------------------------------------------------------------------------------
/Src/Contracts/LoggerInterface.php:
--------------------------------------------------------------------------------
1 | isRunningFromConsole());
23 | self::assertSame('test', $application->getEnvironment());
24 | self::assertNotNull($application->getLogPath());
25 | self::assertInstanceOf(\DateTime::class, $application->getServerTime());
26 | }
27 | }
--------------------------------------------------------------------------------
/Src/Exception/BaseException.php:
--------------------------------------------------------------------------------
1 | data = $data;
23 | parent::__construct($message, $code, $previous);
24 | }
25 |
26 | public function setExtraData(string $key, $value):void
27 | {
28 | $this->data[$key] = $value;
29 | }
30 |
31 | public function getExtraData(): array
32 | {
33 | if(count($this->data) === 0){
34 | return $this->data;
35 | }
36 | return json_decode(json_encode($this->data), true);
37 | }
38 | }
--------------------------------------------------------------------------------
/Src/Helpers/Config.php:
--------------------------------------------------------------------------------
1 | $statusCode, 'content' => $response]);
22 | }
23 |
24 | public function get(string $url)
25 | {
26 | $handler = curl_init();
27 | curl_setopt($handler, CURLOPT_URL, $url);
28 | curl_setopt($handler, CURLOPT_RETURNTRANSFER, true);
29 |
30 | $response = curl_exec($handler);
31 | $statusCode = curl_getinfo($handler, CURLINFO_HTTP_CODE);
32 | curl_close($handler);
33 |
34 | return json_encode(['statusCode' => $statusCode, 'content' => $response]);
35 | }
36 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Osayawe Ogbemudia Terry
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Src/Database/AbstractConnection.php:
--------------------------------------------------------------------------------
1 | credentials = $credentials;
21 | if(!$this->credentialsHaveRequiredKeys($this->credentials)) {
22 | throw new MissingArgumentException(
23 | sprintf(
24 | 'Database connection credentials are not mapped correctly, required key: %s',
25 | implode(',', static::REQUIRED_CONNECTION_KEYS)
26 | )
27 | );
28 | }
29 | }
30 |
31 | private function credentialsHaveRequiredKeys(array $credentials): bool
32 | {
33 | $matches = array_intersect(static::REQUIRED_CONNECTION_KEYS, array_keys($credentials));
34 | return count($matches) === count(static::REQUIRED_CONNECTION_KEYS);
35 | }
36 |
37 | abstract protected function parseCredentials(array $credentials): array;
38 | }
--------------------------------------------------------------------------------
/Src/Database/PDOQueryBuilder.php:
--------------------------------------------------------------------------------
1 | statement->fetchAll();
15 | }
16 |
17 | public function count()
18 | {
19 | return $this->statement->rowCount();
20 | }
21 |
22 | public function lastInsertedId()
23 | {
24 | return $this->connection->lastInsertId();
25 | }
26 |
27 | public function prepare($query)
28 | {
29 | return $this->connection->prepare($query);
30 | }
31 |
32 | public function execute($statement)
33 | {
34 | $statement->execute($this->bindings);
35 | $this->bindings = [];
36 | $this->placeholders = [];
37 | return $statement;
38 | }
39 |
40 | public function fetchInto($className)
41 | {
42 | return $this->statement->fetchAll(PDO::FETCH_CLASS, $className);
43 | }
44 |
45 | public function beginTransaction()
46 | {
47 | $this->connection->beginTransaction();
48 | }
49 |
50 | public function affected()
51 | {
52 | return $this->count();
53 | }
54 | }
--------------------------------------------------------------------------------
/Src/delete.php:
--------------------------------------------------------------------------------
1 | find((int) $reportId);
22 | $repository->delete($bugReport);
23 |
24 | }catch (Throwable $exception){
25 | $logger->critical($exception->getMessage(), $_POST);
26 | throw new BadRequestException($exception->getMessage(), [$exception], 400);
27 | }
28 |
29 | $logger->info(
30 | 'bug report deleted',
31 | ['id' => $bugReport->getId(), 'type' => $bugReport->getReportType(),]
32 | );
33 | $bugReports = $repository->findAll();
34 | }
35 |
--------------------------------------------------------------------------------
/Src/Database/Query.php:
--------------------------------------------------------------------------------
1 | fields, $this->table, implode(' and ', $this->placeholders)
18 | );
19 | break;
20 | case self::DML_TYPE_INSERT:
21 | return sprintf(
22 | "INSERT INTO %s (%s) VALUES (%s)",
23 | $this->table, $this->fields, implode(',', $this->placeholders)
24 | );
25 | break;
26 | case self::DML_TYPE_UPDATE:
27 | return sprintf(
28 | "UPDATE %s SET %s WHERE %s",
29 | $this->table, implode(', ', $this->fields), implode(' and ', $this->placeholders)
30 | );
31 | break;
32 | case self::DML_TYPE_DELETE:
33 | return sprintf(
34 | "DELETE FROM %s WHERE %s",
35 | $this->table, implode(' and ', $this->placeholders)
36 | );
37 | break;
38 | default:
39 | throw new \InvalidArgumentException('Dml type not supported');
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/Src/Helpers/DbQueryBuilderFactory.php:
--------------------------------------------------------------------------------
1 | connect();
29 | return new PDOQueryBuilder($connection);
30 | break;
31 | case 'mysqli':
32 | $connection = (new MySQLiConnection($credentials))->connect();
33 | return new MySQLiQueryBuilder($connection);
34 | break;
35 | default:
36 | throw new DatabaseConnectionException(
37 | "Connection type is not recognize internally", [
38 | 'type' => $connectionType
39 | ]
40 | );
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/Src/add.php:
--------------------------------------------------------------------------------
1 | setReportType($reportType);
19 | $bugReport->setEmail($email);
20 | $bugReport->setLink($link);
21 | $bugReport->setMessage($message);
22 |
23 | $logger = new Logger;
24 | try{
25 | /** @var QueryBuilder $queryBuilder */
26 | $queryBuilder = DbQueryBuilderFactory::make();
27 | /** @var BugReportRepository $repository */
28 | $repository = new BugReportRepository($queryBuilder);
29 | /** @var BugReport $newReport */
30 | $newReport = $repository->create($bugReport);
31 | }catch (Throwable $exception){
32 | $logger->critical($exception->getMessage(), $_POST);
33 | throw new BadRequestException($exception->getMessage(), [$exception], 400);
34 | }
35 |
36 | $logger->info(
37 | 'new bug report created',
38 | ['id' => $newReport->getId(), 'type' => $newReport->getReportType(),]
39 | );
40 | $bugReports = $repository->findAll();
41 | }
--------------------------------------------------------------------------------
/Src/Helpers/App.php:
--------------------------------------------------------------------------------
1 | config = Config::get('app');
17 | }
18 |
19 | public function isDebugMode(): bool
20 | {
21 | if(!isset($this->config['debug'])){
22 | return false;
23 | }
24 | return $this->config['debug'];
25 | }
26 |
27 | public function getEnvironment(): string
28 | {
29 | if(!isset($this->config['env'])){
30 | return 'production';
31 | }
32 | return $this->isTestMode() ? 'test' : $this->config['env'];
33 | }
34 |
35 | public function getLogPath(): string
36 | {
37 | if(!isset($this->config['log_path'])){
38 | throw new \Exception('Log path is not defined');
39 | }
40 | return $this->config['log_path'];
41 | }
42 |
43 | public function isRunningFromConsole(): bool
44 | {
45 | return php_sapi_name() == 'cli' || php_sapi_name() == 'phpbg';
46 | }
47 |
48 | public function getServerTime(): DateTimeInterface
49 | {
50 | return new DateTime('now', new DateTimeZone('Europe/Berlin'));
51 | }
52 |
53 | public function isTestMode(): bool
54 | {
55 | if($this->isRunningFromConsole() && defined('PHPUNIT_RUNNING') && PHPUNIT_RUNNING == true) {
56 | return true;
57 | }
58 | return false;
59 | }
60 | }
--------------------------------------------------------------------------------
/Src/update.php:
--------------------------------------------------------------------------------
1 | find((int) $reportId);
26 | $bugReport->setReportType($reportType);
27 | $bugReport->setEmail($email);
28 | $bugReport->setLink($link);
29 | $bugReport->setMessage($message);
30 | $newReport = $repository->update($bugReport);
31 |
32 | }catch (Throwable $exception){
33 | $logger->critical($exception->getMessage(), $_POST);
34 | throw new BadRequestException($exception->getMessage(), [$exception], 400);
35 | }
36 |
37 | $logger->info(
38 | 'bug report updated',
39 | ['id' => $newReport->getId(), 'type' => $newReport->getReportType(),]
40 | );
41 | $bugReports = $repository->findAll();
42 | }
43 |
--------------------------------------------------------------------------------
/Src/Database/MySQLiConnection.php:
--------------------------------------------------------------------------------
1 | report_mode = MYSQLI_REPORT_STRICT | MYSQLI_REPORT_ERROR;
38 | $credentials = $this->parseCredentials($this->credentials);
39 | try{
40 | $this->connection = new mysqli(...$credentials);
41 | }catch (Throwable $exception){
42 | throw new DatabaseConnectionException(
43 | $exception->getMessage(),
44 | $this->credentials,
45 | 500
46 | );
47 | }
48 | return $this;
49 | }
50 |
51 | public function getConnection(): mysqli
52 | {
53 | return $this->connection;
54 | }
55 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bug-report-app
2 |
3 | Build A bug Tracking App with PHP OOP no Dependencies
4 |
5 | Your support is needed, [Enrol in Course](https://devscreencast.com/courses/build-a-bug-tracking-app-with-php-oop-no-dependencies).
6 |
7 | Object Oriented PHP & TDD with PHPUnit from Scratch
8 |
9 | As a PHP developer it won't take long to realize that there are dozens of outdated tutorials and articles on the internet, if you want to learn how to write better code, with fewer bugs, you will need to know how to write clean object oriented code and unit tests.
10 |
11 | This course is designed to get you up and running as fast as possible with Test Driven Development with object oriented PHP and PHPUnit without any dependencies. We'll quickly cover OOP basics, then dive into some of the more advanced features of the language. Don't be tricked by other courses that only teach you basic and outdated stuffs!
12 |
13 | This is the only course that will teach you Test Driven Development in Object Oriented PHP and PHPUnit. This course will give you ample opportunities to strike out on your own and start working on your own programs.
14 |
15 | In this course you will:
16 |
17 | Understand Object Oriented PHP
18 |
19 | Learn test-driven development (TDD)
20 |
21 | Learn how and why you should use dependency inversion principle and dependency injection
22 |
23 | Implement some common design patterns using PHP
24 |
25 | Build a database wrapper with support for PDO and MySQLi
26 |
27 | Gain a sense of when to use basic language features
28 |
29 | PHP is one of the most popular and widely used programming languages. Get job-ready with PHP today by enrolling now!
30 |
--------------------------------------------------------------------------------
/Src/Database/PDOConnection.php:
--------------------------------------------------------------------------------
1 | parseCredentials($this->credentials);
27 | try{
28 | $this->connection = new PDO(...$credentials);
29 | $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
30 | $this->connection->setAttribute(
31 | PDO::ATTR_DEFAULT_FETCH_MODE,
32 | $this->credentials['default_fetch']
33 | );
34 | }catch (PDOException $exception){
35 | throw new DatabaseConnectionException($exception->getMessage(), $this->credentials, 500);
36 | }
37 | return $this;
38 | }
39 |
40 | public function getConnection(): PDO
41 | {
42 | return $this->connection;
43 | }
44 |
45 | protected function parseCredentials(array $credentials): array
46 | {
47 | $dsn = sprintf(
48 | '%s:host=%s;dbname=%s',
49 | $credentials['driver'],
50 | $credentials['host'],
51 | $credentials['db_name']
52 | );
53 |
54 | return [$dsn, $credentials['db_username'], $credentials['db_user_password']];
55 | }
56 | }
--------------------------------------------------------------------------------
/Tests/Units/LoggerTest.php:
--------------------------------------------------------------------------------
1 | logger = new Logger;
24 | parent::setUp();
25 | }
26 |
27 | public function testItImplementsTheLoggerInterface()
28 | {
29 | self::assertInstanceOf(LoggerInterface::class, $this->logger);
30 | }
31 |
32 | public function testItCanCreateDifferentTypesOfLogLevel()
33 | {
34 | $this->logger->info('Testing Info logs');
35 | $this->logger->error('Testing Error logs');
36 | $this->logger->log(LogLevel::ALERT, 'Testing Alert logs');
37 | $app = new App;
38 |
39 | $fileName = sprintf("%s/%s-%s.log", $app->getLogPath(), 'test', date("j.n.Y"));
40 | self::assertFileExists($fileName);
41 |
42 | $contentOfLogFile = file_get_contents($fileName);
43 | self::assertStringContainsString('Testing Info logs', $contentOfLogFile);
44 | self::assertStringContainsString('Testing Error logs', $contentOfLogFile);
45 | self::assertStringContainsString(LogLevel::ALERT, $contentOfLogFile);
46 | unlink($fileName);
47 | self::assertFileNotExists($fileName);
48 | }
49 |
50 | public function testItThrowsInvalidLogLevelArgumentExceptionWhenGivenAWrongLogLevel()
51 | {
52 | self::expectException(InvalidLogLevelArgument::class);
53 | $this->logger->log('invalid', 'Testing invalid log level');
54 | }
55 | }
--------------------------------------------------------------------------------
/Src/Entity/BugReport.php:
--------------------------------------------------------------------------------
1 | id;
19 | }
20 |
21 | public function setReportType(string $reportType): BugReport
22 | {
23 | $this->report_type = $reportType;
24 | return $this;
25 | }
26 |
27 | public function getReportType(): string
28 | {
29 | return $this->report_type;
30 | }
31 |
32 | public function getEmail (): string
33 | {
34 | return $this->email;
35 | }
36 |
37 | public function setEmail (string $email): BugReport
38 | {
39 | $this->email = $email;
40 | return $this;
41 | }
42 |
43 | public function getLink (): ?string
44 | {
45 | return $this->link;
46 | }
47 |
48 | public function setLink (?string $link): BugReport
49 | {
50 | $this->link = $link;
51 | return $this;
52 | }
53 |
54 | public function getMessage (): string
55 | {
56 | return $this->message;
57 | }
58 |
59 | public function setMessage (string $message): BugReport
60 | {
61 | $this->message = $message;
62 | return $this;
63 | }
64 |
65 | public function getCreatedAt(): string
66 | {
67 | return $this->created_at;
68 | }
69 |
70 | public function toArray (): array
71 | {
72 | return [
73 | 'report_type' => $this->getReportType(),
74 | 'email' => $this->getEmail(),
75 | 'message' => $this->getMessage(),
76 | 'link' => $this->getLink(),
77 | 'created_at' => date('Y-m-d H:i:s'),
78 | ];
79 | }
80 | }
--------------------------------------------------------------------------------
/Tests/Units/DatabaseConnectionTest.php:
--------------------------------------------------------------------------------
1 | getCredentials('pdo');
28 | $pdoHandler = (new PDOConnection($credentials))->connect();
29 | self::assertInstanceOf(DatabaseConnectionInterface::class, $pdoHandler);
30 | return $pdoHandler;
31 | }
32 |
33 | /** @depends testItCanConnectToDatabaseWithPdoApi */
34 | public function testItIsAValidPdoConnection(DatabaseConnectionInterface $handler)
35 | {
36 | self::assertInstanceOf(\PDO::class, $handler->getConnection());
37 | }
38 |
39 | public function testItCanConnectToDatabaseWithMysqliApi()
40 | {
41 | $credentials = $this->getCredentials('mysqli');
42 | $handler = (new MySQLiConnection($credentials))->connect();
43 | self::assertInstanceOf(DatabaseConnectionInterface::class, $handler);
44 | return $handler;
45 | }
46 |
47 | /** @depends testItCanConnectToDatabaseWithMysqliApi */
48 | public function testItIsAValidMysqliConnection(DatabaseConnectionInterface $handler)
49 | {
50 | self::assertInstanceOf(\mysqli::class, $handler->getConnection());
51 | }
52 |
53 | private function getCredentials(string $type)
54 | {
55 | return array_merge(
56 | Config::get('database', $type),
57 | ['db_name' => 'bug_app_testing']
58 | );
59 | }
60 | }
--------------------------------------------------------------------------------
/addModal.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
41 |
42 |
--------------------------------------------------------------------------------
/Src/Logger/Logger.php:
--------------------------------------------------------------------------------
1 | addRecord(LogLevel::EMERGENCY, $message, $context);
19 | }
20 |
21 | public function alert(string $message, array $context = [])
22 | {
23 | $this->addRecord(LogLevel::ALERT, $message, $context);
24 | }
25 |
26 | public function critical(string $message, array $context = [])
27 | {
28 | $this->addRecord(LogLevel::CRITICAL, $message, $context);
29 | }
30 |
31 | public function error(string $message, array $context = [])
32 | {
33 | $this->addRecord(LogLevel::ERROR, $message, $context);
34 | }
35 |
36 | public function warning(string $message, array $context = [])
37 | {
38 | $this->addRecord(LogLevel::WARNING, $message, $context);
39 | }
40 |
41 | public function notice(string $message, array $context = [])
42 | {
43 | $this->addRecord(LogLevel::NOTICE, $message, $context);
44 | }
45 |
46 | public function info(string $message, array $context = [])
47 | {
48 | $this->addRecord(LogLevel::INFO, $message, $context);
49 | }
50 |
51 | public function debug(string $message, array $context = [])
52 | {
53 | $this->addRecord(LogLevel::DEBUG, $message, $context);
54 | }
55 |
56 | public function log(string $level, string $message, array $context = [])
57 | {
58 | $object = new ReflectionClass(LogLevel::class);
59 | $validLogLevelsArray = $object->getConstants();
60 | if(!in_array($level, $validLogLevelsArray)){
61 | throw new InvalidLogLevelArgument($level, $validLogLevelsArray);
62 | }
63 | $this->addRecord($level, $message, $context);
64 | }
65 |
66 | private function addRecord(string $level, string $message, array $context = [])
67 | {
68 | $application = new App;
69 | $date = $application->getServerTime()->format('Y-m-d H:i:s');
70 | $logPath = $application->getLogPath();
71 | $env = $application->getEnvironment();
72 | $details = sprintf(
73 | "%s - Level: %s - Message: %s - Context: %s", $date, $level, $message, json_encode($context)
74 | ).PHP_EOL;
75 |
76 | $fileName = sprintf("%s/%s-%s.log", $logPath, $env, date("j.n.Y"));
77 | file_put_contents($fileName, $details, FILE_APPEND);
78 | }
79 | }
--------------------------------------------------------------------------------
/Src/Repository/Repository.php:
--------------------------------------------------------------------------------
1 | queryBuilder = $queryBuilder;
22 | }
23 |
24 | public function find (int $id): ?object
25 | {
26 | return $this->findOneBy('id', $id);
27 | }
28 |
29 | public function findOneBy (string $field, $value): ?object
30 | {
31 | $result = $this->queryBuilder
32 | ->table(static::$table)
33 | ->select()
34 | ->where($field, $value)
35 | ->runQuery()
36 | ->fetchInto(static::$className);
37 | return ($result) ? $result[0] : null;
38 | }
39 |
40 | public function findBy (array $criteria)
41 | {
42 | $this->queryBuilder->table(static::$table)->select();
43 | foreach ($criteria as $criterion){
44 | $this->queryBuilder->where(...$criterion);
45 | }
46 | return $this->queryBuilder->runQuery()->fetchInto(static::$className);
47 | }
48 |
49 | public function findAll ()
50 | {
51 | return $this->queryBuilder
52 | ->raw("SELECT * FROM reports")
53 | ->fetchInto(static::$className);
54 | }
55 |
56 | public function sql (string $query)
57 | {
58 | return $this->queryBuilder->raw($query)->fetchInto(static::$className);
59 | }
60 |
61 | public function create (Entity $entity): object
62 | {
63 | $id = $this->queryBuilder->table(static::$table)->create($entity->toArray());
64 | return $this->find($id);
65 | }
66 |
67 | public function update (Entity $entity, array $conditions = []): object
68 | {
69 | $this->queryBuilder->table(static::$table)->update($entity->toArray());
70 | foreach ($conditions as $condition){
71 | $this->queryBuilder->where(...$condition);
72 | }
73 | $this->queryBuilder->where('id', $entity->getId())->runQuery();
74 | return $this->find($entity->getId());
75 | }
76 |
77 | public function delete (Entity $entity, array $conditions = []): void
78 | {
79 | $this->queryBuilder->table(static::$table)->delete($entity->toArray());
80 | foreach ($conditions as $condition){
81 | $this->queryBuilder->where(...$condition);
82 | }
83 | $this->queryBuilder->where('id', $entity->getId())->runQuery();
84 | }
85 | }
--------------------------------------------------------------------------------
/Tests/Units/RepositoryTest.php:
--------------------------------------------------------------------------------
1 | queryBuilder = DbQueryBuilderFactory::make(
23 | 'database', 'pdo', ['db_name' => 'bug_app_testing']
24 | );
25 | $this->queryBuilder->beginTransaction();
26 |
27 | $this->bugReportRepository = new BugReportRepository($this->queryBuilder);
28 | parent::setUp();
29 | }
30 |
31 | public function testItCanCreateRecordWithEntity()
32 | {
33 | $newBugReport = $this->createBugReport();
34 | self::assertInstanceOf(BugReport::class, $newBugReport);
35 | self::assertSame('Type 2', $newBugReport->getReportType());
36 | self::assertSame('https://testing-link.com', $newBugReport->getLink());
37 | self::assertSame('This is a dummy message', $newBugReport->getMessage());
38 | self::assertSame('email@test.com', $newBugReport->getEmail());
39 | }
40 |
41 | public function testItCanUpdateAGivenEntity()
42 | {
43 | $newBugReport = $this->createBugReport();
44 | $bugReport = $this->bugReportRepository->find($newBugReport->getId());
45 | $bugReport
46 | ->setMessage('this is from update method')
47 | ->setLink('https://newlink.com/image.png');
48 | $updatedReport = $this->bugReportRepository->update($bugReport);
49 |
50 | self::assertInstanceOf(BugReport::class, $updatedReport);
51 | self::assertSame('this is from update method', $updatedReport->getMessage());
52 | self::assertSame('https://newlink.com/image.png', $updatedReport->getLink());
53 | }
54 |
55 | public function testItCanDeleteAGivenEntity()
56 | {
57 | $newBugReport = $this->createBugReport();
58 | $this->bugReportRepository->delete($newBugReport);
59 | $bugReport = $this->bugReportRepository->find($newBugReport->getId());
60 | self::assertNull($bugReport);
61 | }
62 |
63 | public function testItCanFindByCriteria()
64 | {
65 | $newBugReport = $this->createBugReport();
66 | $report = $this->bugReportRepository->findBy([
67 | ['report_type', '=', 'Type 2',],
68 | ['email', 'email@test.com',],
69 | ]);
70 | self::assertIsArray($report);
71 |
72 | /** @var BugReport $bugReport */
73 | $bugReport = $report[0];
74 | self::assertSame('Type 2', $bugReport->getReportType());
75 | self::assertSame('email@test.com', $bugReport->getEmail());
76 | }
77 |
78 | private function createBugReport(): BugReport
79 | {
80 | $bugReport = new BugReport();
81 | $bugReport->setReportType('Type 2')
82 | ->setLink('https://testing-link.com')
83 | ->setMessage('This is a dummy message')
84 | ->setEmail('email@test.com');
85 |
86 | return $this->bugReportRepository->create($bugReport);
87 | }
88 |
89 | public function tearDown()
90 | {
91 | $this->queryBuilder->rollback();
92 | parent::tearDown();
93 | }
94 | }
--------------------------------------------------------------------------------
/Src/Database/MySQLiQueryBuilder.php:
--------------------------------------------------------------------------------
1 | resultSet){
23 | $this->resultSet = $this->statement->get_result();
24 | if($this->resultSet){
25 | while($object = $this->resultSet->fetch_object()){
26 | $results[] = $object;
27 | }
28 | $this->results = $results;
29 | }
30 | }
31 | return $this->results;
32 | }
33 |
34 | public function count()
35 | {
36 | if(!$this->resultSet) {
37 | $this->get();
38 | }
39 | return $this->resultSet ? $this->resultSet->num_rows : false;
40 | }
41 |
42 | public function lastInsertedId()
43 | {
44 | return $this->connection->insert_id;
45 | }
46 |
47 | public function prepare($query)
48 | {
49 | return $this->connection->prepare($query);
50 | }
51 |
52 | public function execute($statement)
53 | {
54 | if(!$statement) {
55 | throw new InvalidArgumentException('MySQLi statement is false');
56 | }
57 |
58 | if($this->bindings){
59 | $bindings = $this->parseBindings($this->bindings);
60 | $reflectionObj = new \ReflectionClass('mysqli_stmt');
61 | $method = $reflectionObj->getMethod('bind_param');
62 | $method->invokeArgs($statement, $bindings);
63 | }
64 | $statement->execute();
65 | $this->bindings = [];
66 | $this->placeholders = [];
67 |
68 | return $statement;
69 | }
70 |
71 | private function parseBindings(array $params)
72 | {
73 | $bindings = [];
74 | $count = count($params);
75 | if($count === 0){
76 | return $this->bindings;
77 | }
78 |
79 | $bindingTypes = $this->parseBindingTypes(); //"sids"
80 | $bindings[] = & $bindingTypes;
81 | for($index = 0; $index < $count; $index++){
82 | $bindings[] = & $params[$index];
83 | }
84 | return $bindings;
85 | }
86 |
87 | public function parseBindingTypes()
88 | {
89 | $bindingTypes = [];
90 | foreach ($this->bindings as $binding){
91 | if(is_int($binding)){
92 | $bindingTypes[] = self::PARAM_TYPE_INT;
93 | }
94 | if(is_string($binding)){
95 | $bindingTypes[] = self::PARAM_TYPE_STRING;
96 | }
97 |
98 | if(is_float($binding)){
99 | $bindingTypes[] = self::PARAM_TYPE_DOUBLE;
100 | }
101 | }
102 | return implode('', $bindingTypes);
103 | }
104 |
105 | public function fetchInto($className)
106 | {
107 | $results = [];
108 | $this->resultSet = $this->statement->get_result();
109 | while($object = $this->resultSet->fetch_object($className)){
110 | $results[] = $object;
111 | }
112 | return $this->results = $results;
113 | }
114 |
115 | public function beginTransaction()
116 | {
117 | $this->connection->begin_transaction();
118 | }
119 |
120 | public function affected()
121 | {
122 | $this->statement->store_result();
123 | return $this->statement->affected_rows;
124 | }
125 | }
--------------------------------------------------------------------------------
/Tests/Functional/CrudTest.php:
--------------------------------------------------------------------------------
1 | queryBuilder = DbQueryBuilderFactory::make();
26 | $this->repository = new BugReportRepository($this->queryBuilder);
27 | $this->client = new HttpClient();
28 | parent::setUp();
29 | }
30 |
31 | public function testItCanCreateReportUsingPostRequest()
32 | {
33 | $postData = $this->getPostData(['add' => true]);
34 | $response = $this->client->post("http://localhost/bug-report-app/Src/add.php", $postData);
35 | $response = json_decode($response, true);
36 | self::assertEquals(200, $response['statusCode']);
37 |
38 | $result = $this->repository->findBy([
39 | ['report_type', '=', 'Audio'],
40 | ['link', '=', 'https://example.com'],
41 | ['email', '=', 'test@example.com'],
42 | ]);
43 |
44 | /** @var BugReport $bugReport */
45 | $bugReport = $result[0] ?? [];
46 | self::assertInstanceOf(BugReport::class, $bugReport);
47 | self::assertSame('Audio', $bugReport->getReportType());
48 | self::assertSame('https://example.com', $bugReport->getLink());
49 | self::assertSame('test@example.com', $bugReport->getEmail());
50 |
51 | return $bugReport;
52 | }
53 |
54 | /** @depends testItCanCreateReportUsingPostRequest */
55 | public function testItCanUpdateReportUsingPostRequest(BugReport $bugReport)
56 | {
57 | $postData = $this->getPostData([
58 | 'update' => true,
59 | 'message' => 'The video on PHP OOP has audio issues, please check and fix it',
60 | 'link' => 'https://updated.com',
61 | 'reportId' => $bugReport->getId(),
62 | ]);
63 | $response = $this->client->post("http://localhost/bug-report-app/Src/update.php", $postData);
64 | $response = json_decode($response, true);
65 | self::assertEquals(200, $response['statusCode']);
66 |
67 | /** @var BugReport $result */
68 | $result = $this->repository->find($bugReport->getId());
69 |
70 | self::assertInstanceOf(BugReport::class, $result);
71 | self::assertSame(
72 | 'The video on PHP OOP has audio issues, please check and fix it',
73 | $result->getMessage()
74 | );
75 | self::assertSame('https://updated.com', $result->getLink());
76 |
77 | return $result;
78 | }
79 |
80 | /** @depends testItCanUpdateReportUsingPostRequest */
81 | public function testItCanDeleteReportUsingPostRequest(BugReport $bugReport)
82 | {
83 | $postData = [
84 | 'delete' => true,
85 | 'reportId' => $bugReport->getId(),
86 | ];
87 | $response = $this->client->post("http://localhost/bug-report-app/Src/delete.php", $postData);
88 | $response = json_decode($response, true);
89 | self::assertEquals(200, $response['statusCode']);
90 |
91 | /** @var BugReport $result */
92 | $result = $this->repository->find($bugReport->getId());
93 | self::assertNull($result);
94 | }
95 |
96 | private function getPostData(array $options = []): array
97 | {
98 | return array_merge([
99 | 'reportType' => 'Audio',
100 | 'message' => 'The video on xxx has audio issues, please check and fix it',
101 | 'email' => 'test@example.com',
102 | 'link' => 'https://example.com',
103 | ], $options);
104 | }
105 | }
--------------------------------------------------------------------------------
/Tests/Units/QueryBuilderTest.php:
--------------------------------------------------------------------------------
1 | queryBuilder = DbQueryBuilderFactory::make(
21 | 'database', 'pdo', ['db_name' => 'bug_app_testing']
22 | );
23 | $this->queryBuilder->beginTransaction();
24 | parent::setUp();
25 | }
26 |
27 | public function testItCanCreateRecords()
28 | {
29 | $id = $this->insertIntoTable();
30 | self::assertNotNull($id);
31 | }
32 |
33 | public function testInCanPerformRawQuery()
34 | {
35 | $id = $this->insertIntoTable();
36 | $result = $this->queryBuilder->raw("SELECT * FROM reports;")->get();
37 | self::assertNotNull($result);
38 | }
39 |
40 | public function testItCanPerformSelectQuery()
41 | {
42 | $id = $this->insertIntoTable();
43 | $result = $this->queryBuilder
44 | ->table('reports')
45 | ->select('*')
46 | ->where('id', $id)
47 | ->runQuery()
48 | ->first();
49 |
50 | self::assertNotNull($result);
51 | self::assertSame($id, (int) $result->id);
52 | }
53 |
54 | public function testItCanPerformSelectQueryWithMultipleWhereClause()
55 | {
56 | $id = $this->insertIntoTable();
57 | $result = $this->queryBuilder
58 | ->table('reports')
59 | ->select('*')
60 | ->where('id', $id)
61 | ->where('report_type', 'Report Type 1')
62 | ->runQuery()
63 | ->first();
64 | self::assertNotNull($result);
65 | self::assertSame($id, (int) $result->id);
66 | self::assertSame('Report Type 1', $result->report_type);
67 | }
68 |
69 | public function testItCanFindById()
70 | {
71 | $id = $this->insertIntoTable();
72 | $result = $this->queryBuilder->table('reports')
73 | ->select('*')->find($id);
74 | self::assertNotNull($result);
75 | self::assertSame($id, (int) $result->id);
76 | self::assertSame('Report Type 1', $result->report_type);
77 | }
78 |
79 | public function testItCanFindOneByGivenValue()
80 | {
81 | $id = $this->insertIntoTable();
82 | $result = $this->queryBuilder->table('reports')
83 | ->select('*')->findOneBy('report_type', 'Report Type 1');
84 | self::assertNotNull($result);
85 | self::assertSame($id, (int) $result->id);
86 | self::assertSame('Report Type 1', $result->report_type);
87 | }
88 |
89 | public function testItCanUpdateGivenRecord()
90 | {
91 | $id = $this->insertIntoTable();
92 | $count = $this->queryBuilder->table('reports')->update([
93 | 'report_type' => 'Report Type 1 updated'
94 | ])->where('id', $id)->runQuery()->affected();
95 |
96 | self::assertEquals(1, $count);
97 | $result = $this->queryBuilder->select('*')->find($id);
98 | self::assertNotNull($result);
99 | self::assertSame($id, (int) $result->id);
100 | self::assertSame('Report Type 1 updated', $result->report_type);
101 | }
102 | public function testItCanDeleteGivenId()
103 | {
104 | $id = $this->insertIntoTable();
105 | $count = $this->queryBuilder->table('reports')->delete()
106 | ->where('id', $id)->runQuery()->affected();
107 |
108 | self::assertEquals(1, $count);
109 | $result = $this->queryBuilder->select('*')->find($id);
110 | self::assertNull($result);
111 | }
112 |
113 |
114 | public function tearDown()
115 | {
116 | $this->queryBuilder->rollback();
117 | parent::tearDown();
118 | }
119 |
120 | /**
121 | * @return mixed
122 | */
123 | private function insertIntoTable()
124 | {
125 | $data = [
126 | 'report_type' => 'Report Type 1',
127 | 'message' => 'This is a dummy message',
128 | 'email' => 'support@devscreencast',
129 | 'link' => 'https://link.com',
130 | 'created_at' => date('Y-m-d H:i:s'),
131 | ];
132 | return $this->queryBuilder->table('reports')->create($data);
133 | }
134 | }
--------------------------------------------------------------------------------
/Src/Database/QueryBuilder.php:
--------------------------------------------------------------------------------
1 | =', '>', '<=', '<', '<>'];
22 | const PLACEHOLDER = '?';
23 | const COLUMNS = '*';
24 | const DML_TYPE_SELECT = 'SELECT';
25 | const DML_TYPE_INSERT = 'INSERT';
26 | const DML_TYPE_UPDATE = 'UPDATE';
27 | const DML_TYPE_DELETE = 'DELETE';
28 |
29 | use Query;
30 |
31 | public function __construct(DatabaseConnectionInterface $databaseConnection)
32 | {
33 | $this->connection = $databaseConnection->getConnection();
34 | }
35 |
36 | public function table($table)
37 | {
38 | $this->table = $table;
39 | return $this;
40 | }
41 |
42 | public function where($column, $operator = self::OPERATORS[0], $value = null)
43 | {
44 | if(!in_array($operator, self::OPERATORS)){
45 | if($value === null){
46 | $value = $operator;
47 | $operator = self::OPERATORS[0];
48 | }else{
49 | throw new InvalidArgumentException('Operator is not valid', ['operator' => $operator]);
50 | }
51 | }
52 | $this->parseWhere([$column => $value], $operator);
53 |
54 | return $this;
55 | }
56 |
57 | private function parseWhere(array $conditions, string $operator)
58 | {
59 | foreach ($conditions as $column => $value){
60 | $this->placeholders[] = sprintf('%s %s %s', $column, $operator, self::PLACEHOLDER);
61 | $this->bindings[] = $value;
62 | }
63 | return $this;
64 | }
65 |
66 | public function select(string $fields = self::COLUMNS)
67 | {
68 | $this->operation = self::DML_TYPE_SELECT;
69 | $this->fields = $fields;
70 | return $this;
71 | }
72 |
73 | public function create(array $data)
74 | {
75 | $this->fields = '`' . implode('`, `', array_keys($data)) . '`';
76 | foreach ($data as $value){
77 | $this->placeholders[] = self::PLACEHOLDER;
78 | $this->bindings[] = $value;
79 | }
80 | $query = $this->prepare($this->getQuery(self::DML_TYPE_INSERT));
81 | $this->statement = $this->execute($query);
82 |
83 | return (int) $this->lastInsertedId();
84 | }
85 |
86 | public function update(array $data)
87 | {
88 | $this->fields = [];
89 | $this->operation = self::DML_TYPE_UPDATE;
90 | foreach ($data as $column => $value){
91 | $this->fields[] = sprintf('%s%s%s', $column, self::OPERATORS[0], "'$value'");
92 | }
93 | return $this;
94 | }
95 |
96 | public function delete()
97 | {
98 | $this->operation = self::DML_TYPE_DELETE;
99 | return $this;
100 | }
101 |
102 | public function raw($query)
103 | {
104 | $query = $this->prepare($query);
105 | $this->statement = $this->execute($query);
106 | return $this;
107 | }
108 |
109 | public function find($id)
110 | {
111 | return $this->where('id', $id)->runQuery()->first();
112 | }
113 |
114 | public function findOneBy(string $field, $value)
115 | {
116 | return $this->where($field, $value)->runQuery()->first();
117 | }
118 |
119 | public function first()
120 | {
121 | return $this->count() ? $this->get()[0] : null;
122 | }
123 |
124 | public function rollback(): void
125 | {
126 | $this->connection->rollback();
127 | }
128 |
129 | public function runQuery()
130 | {
131 | $query = $this->prepare($this->getQuery($this->operation));
132 | $this->statement = $this->execute($query);
133 | return $this;
134 | }
135 |
136 | abstract public function get();
137 | abstract public function count();
138 | abstract public function lastInsertedId();
139 | abstract public function prepare($query);
140 | abstract public function execute($statement);
141 | abstract public function fetchInto($className);
142 | abstract public function beginTransaction();
143 | abstract public function affected();
144 | }
--------------------------------------------------------------------------------
/Resources/css/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | color: #566787;
3 | background: #f5f5f5;
4 | font-family: 'Varela Round', sans-serif;
5 | font-size: 13px;
6 | }
7 | .table-wrapper {
8 | background: #fff;
9 | padding: 20px 25px;
10 | margin: 30px 0;
11 | border-radius: 3px;
12 | box-shadow: 0 1px 1px rgba(0,0,0,.05);
13 | }
14 | .table-title {
15 | padding-bottom: 15px;
16 | background: #435d7d;
17 | color: #fff;
18 | padding: 16px 30px;
19 | margin: -20px -25px 10px;
20 | border-radius: 3px 3px 0 0;
21 | }
22 | .table-title h2 {
23 | margin: 5px 0 0;
24 | font-size: 24px;
25 | }
26 | .table-title .btn-group {
27 | float: right;
28 | }
29 | .table-title .btn {
30 | color: #fff;
31 | float: right;
32 | font-size: 13px;
33 | border: none;
34 | min-width: 50px;
35 | border-radius: 2px;
36 | border: none;
37 | outline: none !important;
38 | margin-left: 10px;
39 | }
40 | .table-title .btn i {
41 | float: left;
42 | font-size: 21px;
43 | margin-right: 5px;
44 | }
45 | .table-title .btn span {
46 | float: left;
47 | margin-top: 2px;
48 | }
49 | table.table tr th, table.table tr td {
50 | border-color: #e9e9e9;
51 | padding: 12px 15px;
52 | vertical-align: middle;
53 | }
54 | table.table tr th:first-child {
55 | width: 60px;
56 | }
57 | table.table tr th:last-child {
58 | width: 100px;
59 | }
60 | table.table-striped tbody tr:nth-of-type(odd) {
61 | background-color: #fcfcfc;
62 | }
63 | table.table-striped.table-hover tbody tr:hover {
64 | background: #f5f5f5;
65 | }
66 | table.table th i {
67 | font-size: 13px;
68 | margin: 0 5px;
69 | cursor: pointer;
70 | }
71 | table.table td:last-child i {
72 | opacity: 0.9;
73 | font-size: 22px;
74 | margin: 0 5px;
75 | }
76 | table.table td a {
77 | font-weight: bold;
78 | color: #566787;
79 | display: inline-block;
80 | text-decoration: none;
81 | outline: none !important;
82 | }
83 | table.table td a:hover {
84 | color: #2196F3;
85 | }
86 | table.table td a.edit {
87 | color: #FFC107;
88 | }
89 | table.table td a.delete {
90 | color: #F44336;
91 | }
92 | table.table td i {
93 | font-size: 19px;
94 | }
95 | table.table .avatar {
96 | border-radius: 50%;
97 | vertical-align: middle;
98 | margin-right: 10px;
99 | }
100 | .pagination {
101 | float: right;
102 | margin: 0 0 5px;
103 | }
104 | .pagination li a {
105 | border: none;
106 | font-size: 13px;
107 | min-width: 30px;
108 | min-height: 30px;
109 | color: #999;
110 | margin: 0 2px;
111 | line-height: 30px;
112 | border-radius: 2px !important;
113 | text-align: center;
114 | padding: 0 6px;
115 | }
116 | .pagination li a:hover {
117 | color: #666;
118 | }
119 | .pagination li.active a, .pagination li.active a.page-link {
120 | background: #03A9F4;
121 | }
122 | .pagination li.active a:hover {
123 | background: #0397d6;
124 | }
125 | .pagination li.disabled i {
126 | color: #ccc;
127 | }
128 | .pagination li i {
129 | font-size: 16px;
130 | padding-top: 6px
131 | }
132 | .hint-text {
133 | float: left;
134 | margin-top: 10px;
135 | font-size: 13px;
136 | }
137 | /* Custom checkbox */
138 | .custom-checkbox {
139 | position: relative;
140 | }
141 | .custom-checkbox input[type="checkbox"] {
142 | opacity: 0;
143 | position: absolute;
144 | margin: 5px 0 0 3px;
145 | z-index: 9;
146 | }
147 | .custom-checkbox label:before{
148 | width: 18px;
149 | height: 18px;
150 | }
151 | .custom-checkbox label:before {
152 | content: '';
153 | margin-right: 10px;
154 | display: inline-block;
155 | vertical-align: text-top;
156 | background: white;
157 | border: 1px solid #bbb;
158 | border-radius: 2px;
159 | box-sizing: border-box;
160 | z-index: 2;
161 | }
162 | .custom-checkbox input[type="checkbox"]:checked + label:after {
163 | content: '';
164 | position: absolute;
165 | left: 6px;
166 | top: 3px;
167 | width: 6px;
168 | height: 11px;
169 | border: solid #000;
170 | border-width: 0 3px 3px 0;
171 | transform: inherit;
172 | z-index: 3;
173 | transform: rotateZ(45deg);
174 | }
175 | .custom-checkbox input[type="checkbox"]:checked + label:before {
176 | border-color: #03A9F4;
177 | background: #03A9F4;
178 | }
179 | .custom-checkbox input[type="checkbox"]:checked + label:after {
180 | border-color: #fff;
181 | }
182 | .custom-checkbox input[type="checkbox"]:disabled + label:before {
183 | color: #b8b8b8;
184 | cursor: auto;
185 | box-shadow: none;
186 | background: #ddd;
187 | }
188 | /* Modal styles */
189 | .modal .modal-dialog {
190 | max-width: 400px;
191 | }
192 | .modal .modal-header, .modal .modal-body, .modal .modal-footer {
193 | padding: 20px 30px;
194 | }
195 | .modal .modal-content {
196 | border-radius: 3px;
197 | }
198 | .modal .modal-footer {
199 | background: #ecf0f1;
200 | border-radius: 0 0 3px 3px;
201 | }
202 | .modal .modal-title {
203 | display: inline-block;
204 | }
205 | .modal .form-control {
206 | border-radius: 2px;
207 | box-shadow: none;
208 | border-color: #dddddd;
209 | }
210 | .modal textarea.form-control {
211 | resize: vertical;
212 | }
213 | .modal .btn {
214 | border-radius: 2px;
215 | min-width: 100px;
216 | }
217 | .modal form label {
218 | font-weight: normal;
219 | }
--------------------------------------------------------------------------------
/store.sql:
--------------------------------------------------------------------------------
1 | -- phpMyAdmin SQL Dump
2 | -- version 4.8.3
3 | -- https://www.phpmyadmin.net/
4 | --
5 | -- Host: 127.0.0.1
6 | -- Generation Time: Mar 30, 2019 at 10:11 PM
7 | -- Server version: 10.1.36-MariaDB
8 | -- PHP Version: 7.2.11
9 |
10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
11 | SET AUTOCOMMIT = 0;
12 | START TRANSACTION;
13 | SET time_zone = "+00:00";
14 |
15 |
16 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
17 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
18 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
19 | /*!40101 SET NAMES utf8mb4 */;
20 |
21 | --
22 | -- Database: `store`
23 | --
24 |
25 | -- --------------------------------------------------------
26 |
27 | --
28 | -- Table structure for table `categories`
29 | --
30 |
31 | CREATE TABLE `categories` (
32 | `id` int(11) NOT NULL,
33 | `name` varchar(255) NOT NULL,
34 | `slug` varchar(255) NOT NULL,
35 | `created_at` timestamp NULL DEFAULT NULL,
36 | `updated_at` timestamp NULL DEFAULT NULL,
37 | `deleted_at` timestamp NULL DEFAULT NULL
38 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
39 |
40 | --
41 | -- Dumping data for table `categories`
42 | --
43 |
44 | INSERT INTO `categories` (`id`, `name`, `slug`, `created_at`, `updated_at`, `deleted_at`) VALUES
45 | (1, 'Trousers', 'trousers', '2019-03-27 19:28:22', '2019-03-27 19:28:22', NULL),
46 | (2, 'Bottoms', 'bottoms', '2019-03-27 19:28:38', '2019-03-27 19:28:38', NULL),
47 | (3, 'Tops', 'tops', '2019-03-27 19:28:49', '2019-03-27 19:28:49', NULL),
48 | (4, 'Accessories', 'accessories', '2019-03-27 19:29:01', '2019-03-27 19:29:01', NULL),
49 | (5, 'Shoes', 'shoes', '2019-03-27 19:29:22', '2019-03-27 19:29:22', NULL);
50 |
51 | -- --------------------------------------------------------
52 |
53 | --
54 | -- Table structure for table `orders`
55 | --
56 |
57 | CREATE TABLE `orders` (
58 | `id` int(11) NOT NULL,
59 | `user_id` int(11) NOT NULL,
60 | `product_id` int(11) NOT NULL,
61 | `unit_price` float NOT NULL,
62 | `total` float NOT NULL,
63 | `status` varchar(255) NOT NULL,
64 | `order_no` varchar(255) DEFAULT NULL,
65 | `created_at` timestamp NULL DEFAULT NULL,
66 | `updated_at` timestamp NULL DEFAULT NULL,
67 | `deleted_at` timestamp NULL DEFAULT NULL
68 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
69 |
70 | -- --------------------------------------------------------
71 |
72 | --
73 | -- Table structure for table `payments`
74 | --
75 |
76 | CREATE TABLE `payments` (
77 | `id` int(11) NOT NULL,
78 | `user_id` int(255) NOT NULL,
79 | `order_no` varchar(255) NOT NULL,
80 | `amount` float NOT NULL,
81 | `status` varchar(255) NOT NULL,
82 | `created_at` timestamp NULL DEFAULT NULL,
83 | `updated_at` timestamp NULL DEFAULT NULL,
84 | `deleted_at` timestamp NULL DEFAULT NULL
85 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
86 |
87 | -- --------------------------------------------------------
88 |
89 | --
90 | -- Table structure for table `products`
91 | --
92 |
93 | CREATE TABLE `products` (
94 | `id` int(11) NOT NULL,
95 | `name` varchar(255) NOT NULL,
96 | `price` float NOT NULL,
97 | `description` text NOT NULL,
98 | `category_id` int(11) NOT NULL,
99 | `sub_category_id` int(11) NOT NULL,
100 | `image_path` varchar(255) NOT NULL,
101 | `created_at` timestamp NULL DEFAULT NULL,
102 | `updated_at` timestamp NULL DEFAULT NULL,
103 | `deleted_at` timestamp NULL DEFAULT NULL
104 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
105 |
106 | -- --------------------------------------------------------
107 |
108 | --
109 | -- Table structure for table `sub_categories`
110 | --
111 |
112 | CREATE TABLE `sub_categories` (
113 | `id` int(11) NOT NULL,
114 | `name` varchar(255) NOT NULL,
115 | `slug` varchar(255) NOT NULL,
116 | `category_id` int(11) NOT NULL,
117 | `created_at` timestamp NULL DEFAULT NULL,
118 | `updated_at` timestamp NULL DEFAULT NULL,
119 | `deleted_at` timestamp NULL DEFAULT NULL
120 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
121 |
122 | --
123 | -- Dumping data for table `sub_categories`
124 | --
125 |
126 | INSERT INTO `sub_categories` (`id`, `name`, `slug`, `category_id`, `created_at`, `updated_at`, `deleted_at`) VALUES
127 | (3, 'Sneakers', 'sneakers', 5, '2019-03-29 00:48:16', '2019-03-29 00:48:16', NULL),
128 | (4, 'loafers', 'loafers', 5, '2019-03-29 00:49:20', '2019-03-29 00:49:20', NULL);
129 |
130 | -- --------------------------------------------------------
131 |
132 | --
133 | -- Table structure for table `users`
134 | --
135 |
136 | CREATE TABLE `users` (
137 | `id` int(11) NOT NULL,
138 | `username` varchar(255) NOT NULL,
139 | `fullname` varchar(255) NOT NULL,
140 | `email` varchar(255) NOT NULL,
141 | `password` varchar(255) NOT NULL,
142 | `address` text NOT NULL,
143 | `role` varchar(50) NOT NULL,
144 | `created_at` timestamp NULL DEFAULT NULL,
145 | `updated_at` timestamp NULL DEFAULT NULL,
146 | `deleted_at` timestamp NULL DEFAULT NULL
147 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
148 |
149 | --
150 | -- Indexes for dumped tables
151 | --
152 |
153 | --
154 | -- Indexes for table `categories`
155 | --
156 | ALTER TABLE `categories`
157 | ADD PRIMARY KEY (`id`);
158 |
159 | --
160 | -- Indexes for table `orders`
161 | --
162 | ALTER TABLE `orders`
163 | ADD PRIMARY KEY (`id`);
164 |
165 | --
166 | -- Indexes for table `payments`
167 | --
168 | ALTER TABLE `payments`
169 | ADD PRIMARY KEY (`id`);
170 |
171 | --
172 | -- Indexes for table `products`
173 | --
174 | ALTER TABLE `products`
175 | ADD PRIMARY KEY (`id`),
176 | ADD UNIQUE KEY `name` (`name`);
177 |
178 | --
179 | -- Indexes for table `sub_categories`
180 | --
181 | ALTER TABLE `sub_categories`
182 | ADD PRIMARY KEY (`id`);
183 |
184 | --
185 | -- Indexes for table `users`
186 | --
187 | ALTER TABLE `users`
188 | ADD PRIMARY KEY (`id`);
189 |
190 | --
191 | -- AUTO_INCREMENT for dumped tables
192 | --
193 |
194 | --
195 | -- AUTO_INCREMENT for table `categories`
196 | --
197 | ALTER TABLE `categories`
198 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
199 |
200 | --
201 | -- AUTO_INCREMENT for table `sub_categories`
202 | --
203 | ALTER TABLE `sub_categories`
204 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
205 | COMMIT;
206 |
207 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
208 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
209 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
210 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Bug Report App
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
Manage Bug Reports
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 | | Report Type |
36 | Email |
37 | Message |
38 | Link |
39 | Actions |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | | getReportType(); ?> |
48 | getEmail(); ?> |
49 | getMessage(); ?> |
50 | getLink(); ?> |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
116 |
117 |
118 |
119 | |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "6eb683164343ed0e11c8dd28342af671",
8 | "packages": [],
9 | "packages-dev": [
10 | {
11 | "name": "doctrine/instantiator",
12 | "version": "1.2.0",
13 | "source": {
14 | "type": "git",
15 | "url": "https://github.com/doctrine/instantiator.git",
16 | "reference": "a2c590166b2133a4633738648b6b064edae0814a"
17 | },
18 | "dist": {
19 | "type": "zip",
20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a",
21 | "reference": "a2c590166b2133a4633738648b6b064edae0814a",
22 | "shasum": ""
23 | },
24 | "require": {
25 | "php": "^7.1"
26 | },
27 | "require-dev": {
28 | "doctrine/coding-standard": "^6.0",
29 | "ext-pdo": "*",
30 | "ext-phar": "*",
31 | "phpbench/phpbench": "^0.13",
32 | "phpstan/phpstan-phpunit": "^0.11",
33 | "phpstan/phpstan-shim": "^0.11",
34 | "phpunit/phpunit": "^7.0"
35 | },
36 | "type": "library",
37 | "extra": {
38 | "branch-alias": {
39 | "dev-master": "1.2.x-dev"
40 | }
41 | },
42 | "autoload": {
43 | "psr-4": {
44 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
45 | }
46 | },
47 | "notification-url": "https://packagist.org/downloads/",
48 | "license": [
49 | "MIT"
50 | ],
51 | "authors": [
52 | {
53 | "name": "Marco Pivetta",
54 | "email": "ocramius@gmail.com",
55 | "homepage": "http://ocramius.github.com/"
56 | }
57 | ],
58 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
59 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
60 | "keywords": [
61 | "constructor",
62 | "instantiate"
63 | ],
64 | "time": "2019-03-17T17:37:11+00:00"
65 | },
66 | {
67 | "name": "myclabs/deep-copy",
68 | "version": "1.8.1",
69 | "source": {
70 | "type": "git",
71 | "url": "https://github.com/myclabs/DeepCopy.git",
72 | "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8"
73 | },
74 | "dist": {
75 | "type": "zip",
76 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
77 | "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
78 | "shasum": ""
79 | },
80 | "require": {
81 | "php": "^7.1"
82 | },
83 | "replace": {
84 | "myclabs/deep-copy": "self.version"
85 | },
86 | "require-dev": {
87 | "doctrine/collections": "^1.0",
88 | "doctrine/common": "^2.6",
89 | "phpunit/phpunit": "^7.1"
90 | },
91 | "type": "library",
92 | "autoload": {
93 | "psr-4": {
94 | "DeepCopy\\": "src/DeepCopy/"
95 | },
96 | "files": [
97 | "src/DeepCopy/deep_copy.php"
98 | ]
99 | },
100 | "notification-url": "https://packagist.org/downloads/",
101 | "license": [
102 | "MIT"
103 | ],
104 | "description": "Create deep copies (clones) of your objects",
105 | "keywords": [
106 | "clone",
107 | "copy",
108 | "duplicate",
109 | "object",
110 | "object graph"
111 | ],
112 | "time": "2018-06-11T23:09:50+00:00"
113 | },
114 | {
115 | "name": "phar-io/manifest",
116 | "version": "1.0.3",
117 | "source": {
118 | "type": "git",
119 | "url": "https://github.com/phar-io/manifest.git",
120 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
121 | },
122 | "dist": {
123 | "type": "zip",
124 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
125 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
126 | "shasum": ""
127 | },
128 | "require": {
129 | "ext-dom": "*",
130 | "ext-phar": "*",
131 | "phar-io/version": "^2.0",
132 | "php": "^5.6 || ^7.0"
133 | },
134 | "type": "library",
135 | "extra": {
136 | "branch-alias": {
137 | "dev-master": "1.0.x-dev"
138 | }
139 | },
140 | "autoload": {
141 | "classmap": [
142 | "src/"
143 | ]
144 | },
145 | "notification-url": "https://packagist.org/downloads/",
146 | "license": [
147 | "BSD-3-Clause"
148 | ],
149 | "authors": [
150 | {
151 | "name": "Arne Blankerts",
152 | "email": "arne@blankerts.de",
153 | "role": "Developer"
154 | },
155 | {
156 | "name": "Sebastian Heuer",
157 | "email": "sebastian@phpeople.de",
158 | "role": "Developer"
159 | },
160 | {
161 | "name": "Sebastian Bergmann",
162 | "email": "sebastian@phpunit.de",
163 | "role": "Developer"
164 | }
165 | ],
166 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
167 | "time": "2018-07-08T19:23:20+00:00"
168 | },
169 | {
170 | "name": "phar-io/version",
171 | "version": "2.0.1",
172 | "source": {
173 | "type": "git",
174 | "url": "https://github.com/phar-io/version.git",
175 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6"
176 | },
177 | "dist": {
178 | "type": "zip",
179 | "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6",
180 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6",
181 | "shasum": ""
182 | },
183 | "require": {
184 | "php": "^5.6 || ^7.0"
185 | },
186 | "type": "library",
187 | "autoload": {
188 | "classmap": [
189 | "src/"
190 | ]
191 | },
192 | "notification-url": "https://packagist.org/downloads/",
193 | "license": [
194 | "BSD-3-Clause"
195 | ],
196 | "authors": [
197 | {
198 | "name": "Arne Blankerts",
199 | "email": "arne@blankerts.de",
200 | "role": "Developer"
201 | },
202 | {
203 | "name": "Sebastian Heuer",
204 | "email": "sebastian@phpeople.de",
205 | "role": "Developer"
206 | },
207 | {
208 | "name": "Sebastian Bergmann",
209 | "email": "sebastian@phpunit.de",
210 | "role": "Developer"
211 | }
212 | ],
213 | "description": "Library for handling version information and constraints",
214 | "time": "2018-07-08T19:19:57+00:00"
215 | },
216 | {
217 | "name": "phpdocumentor/reflection-common",
218 | "version": "1.0.1",
219 | "source": {
220 | "type": "git",
221 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
222 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
223 | },
224 | "dist": {
225 | "type": "zip",
226 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
227 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
228 | "shasum": ""
229 | },
230 | "require": {
231 | "php": ">=5.5"
232 | },
233 | "require-dev": {
234 | "phpunit/phpunit": "^4.6"
235 | },
236 | "type": "library",
237 | "extra": {
238 | "branch-alias": {
239 | "dev-master": "1.0.x-dev"
240 | }
241 | },
242 | "autoload": {
243 | "psr-4": {
244 | "phpDocumentor\\Reflection\\": [
245 | "src"
246 | ]
247 | }
248 | },
249 | "notification-url": "https://packagist.org/downloads/",
250 | "license": [
251 | "MIT"
252 | ],
253 | "authors": [
254 | {
255 | "name": "Jaap van Otterdijk",
256 | "email": "opensource@ijaap.nl"
257 | }
258 | ],
259 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
260 | "homepage": "http://www.phpdoc.org",
261 | "keywords": [
262 | "FQSEN",
263 | "phpDocumentor",
264 | "phpdoc",
265 | "reflection",
266 | "static analysis"
267 | ],
268 | "time": "2017-09-11T18:02:19+00:00"
269 | },
270 | {
271 | "name": "phpdocumentor/reflection-docblock",
272 | "version": "4.3.0",
273 | "source": {
274 | "type": "git",
275 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
276 | "reference": "94fd0001232e47129dd3504189fa1c7225010d08"
277 | },
278 | "dist": {
279 | "type": "zip",
280 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
281 | "reference": "94fd0001232e47129dd3504189fa1c7225010d08",
282 | "shasum": ""
283 | },
284 | "require": {
285 | "php": "^7.0",
286 | "phpdocumentor/reflection-common": "^1.0.0",
287 | "phpdocumentor/type-resolver": "^0.4.0",
288 | "webmozart/assert": "^1.0"
289 | },
290 | "require-dev": {
291 | "doctrine/instantiator": "~1.0.5",
292 | "mockery/mockery": "^1.0",
293 | "phpunit/phpunit": "^6.4"
294 | },
295 | "type": "library",
296 | "extra": {
297 | "branch-alias": {
298 | "dev-master": "4.x-dev"
299 | }
300 | },
301 | "autoload": {
302 | "psr-4": {
303 | "phpDocumentor\\Reflection\\": [
304 | "src/"
305 | ]
306 | }
307 | },
308 | "notification-url": "https://packagist.org/downloads/",
309 | "license": [
310 | "MIT"
311 | ],
312 | "authors": [
313 | {
314 | "name": "Mike van Riel",
315 | "email": "me@mikevanriel.com"
316 | }
317 | ],
318 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
319 | "time": "2017-11-30T07:14:17+00:00"
320 | },
321 | {
322 | "name": "phpdocumentor/type-resolver",
323 | "version": "0.4.0",
324 | "source": {
325 | "type": "git",
326 | "url": "https://github.com/phpDocumentor/TypeResolver.git",
327 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
328 | },
329 | "dist": {
330 | "type": "zip",
331 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
332 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
333 | "shasum": ""
334 | },
335 | "require": {
336 | "php": "^5.5 || ^7.0",
337 | "phpdocumentor/reflection-common": "^1.0"
338 | },
339 | "require-dev": {
340 | "mockery/mockery": "^0.9.4",
341 | "phpunit/phpunit": "^5.2||^4.8.24"
342 | },
343 | "type": "library",
344 | "extra": {
345 | "branch-alias": {
346 | "dev-master": "1.0.x-dev"
347 | }
348 | },
349 | "autoload": {
350 | "psr-4": {
351 | "phpDocumentor\\Reflection\\": [
352 | "src/"
353 | ]
354 | }
355 | },
356 | "notification-url": "https://packagist.org/downloads/",
357 | "license": [
358 | "MIT"
359 | ],
360 | "authors": [
361 | {
362 | "name": "Mike van Riel",
363 | "email": "me@mikevanriel.com"
364 | }
365 | ],
366 | "time": "2017-07-14T14:27:02+00:00"
367 | },
368 | {
369 | "name": "phpspec/prophecy",
370 | "version": "1.8.0",
371 | "source": {
372 | "type": "git",
373 | "url": "https://github.com/phpspec/prophecy.git",
374 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06"
375 | },
376 | "dist": {
377 | "type": "zip",
378 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
379 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
380 | "shasum": ""
381 | },
382 | "require": {
383 | "doctrine/instantiator": "^1.0.2",
384 | "php": "^5.3|^7.0",
385 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
386 | "sebastian/comparator": "^1.1|^2.0|^3.0",
387 | "sebastian/recursion-context": "^1.0|^2.0|^3.0"
388 | },
389 | "require-dev": {
390 | "phpspec/phpspec": "^2.5|^3.2",
391 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
392 | },
393 | "type": "library",
394 | "extra": {
395 | "branch-alias": {
396 | "dev-master": "1.8.x-dev"
397 | }
398 | },
399 | "autoload": {
400 | "psr-0": {
401 | "Prophecy\\": "src/"
402 | }
403 | },
404 | "notification-url": "https://packagist.org/downloads/",
405 | "license": [
406 | "MIT"
407 | ],
408 | "authors": [
409 | {
410 | "name": "Konstantin Kudryashov",
411 | "email": "ever.zet@gmail.com",
412 | "homepage": "http://everzet.com"
413 | },
414 | {
415 | "name": "Marcello Duarte",
416 | "email": "marcello.duarte@gmail.com"
417 | }
418 | ],
419 | "description": "Highly opinionated mocking framework for PHP 5.3+",
420 | "homepage": "https://github.com/phpspec/prophecy",
421 | "keywords": [
422 | "Double",
423 | "Dummy",
424 | "fake",
425 | "mock",
426 | "spy",
427 | "stub"
428 | ],
429 | "time": "2018-08-05T17:53:17+00:00"
430 | },
431 | {
432 | "name": "phpunit/php-code-coverage",
433 | "version": "6.1.4",
434 | "source": {
435 | "type": "git",
436 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
437 | "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d"
438 | },
439 | "dist": {
440 | "type": "zip",
441 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d",
442 | "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d",
443 | "shasum": ""
444 | },
445 | "require": {
446 | "ext-dom": "*",
447 | "ext-xmlwriter": "*",
448 | "php": "^7.1",
449 | "phpunit/php-file-iterator": "^2.0",
450 | "phpunit/php-text-template": "^1.2.1",
451 | "phpunit/php-token-stream": "^3.0",
452 | "sebastian/code-unit-reverse-lookup": "^1.0.1",
453 | "sebastian/environment": "^3.1 || ^4.0",
454 | "sebastian/version": "^2.0.1",
455 | "theseer/tokenizer": "^1.1"
456 | },
457 | "require-dev": {
458 | "phpunit/phpunit": "^7.0"
459 | },
460 | "suggest": {
461 | "ext-xdebug": "^2.6.0"
462 | },
463 | "type": "library",
464 | "extra": {
465 | "branch-alias": {
466 | "dev-master": "6.1-dev"
467 | }
468 | },
469 | "autoload": {
470 | "classmap": [
471 | "src/"
472 | ]
473 | },
474 | "notification-url": "https://packagist.org/downloads/",
475 | "license": [
476 | "BSD-3-Clause"
477 | ],
478 | "authors": [
479 | {
480 | "name": "Sebastian Bergmann",
481 | "email": "sebastian@phpunit.de",
482 | "role": "lead"
483 | }
484 | ],
485 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
486 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
487 | "keywords": [
488 | "coverage",
489 | "testing",
490 | "xunit"
491 | ],
492 | "time": "2018-10-31T16:06:48+00:00"
493 | },
494 | {
495 | "name": "phpunit/php-file-iterator",
496 | "version": "2.0.2",
497 | "source": {
498 | "type": "git",
499 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
500 | "reference": "050bedf145a257b1ff02746c31894800e5122946"
501 | },
502 | "dist": {
503 | "type": "zip",
504 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
505 | "reference": "050bedf145a257b1ff02746c31894800e5122946",
506 | "shasum": ""
507 | },
508 | "require": {
509 | "php": "^7.1"
510 | },
511 | "require-dev": {
512 | "phpunit/phpunit": "^7.1"
513 | },
514 | "type": "library",
515 | "extra": {
516 | "branch-alias": {
517 | "dev-master": "2.0.x-dev"
518 | }
519 | },
520 | "autoload": {
521 | "classmap": [
522 | "src/"
523 | ]
524 | },
525 | "notification-url": "https://packagist.org/downloads/",
526 | "license": [
527 | "BSD-3-Clause"
528 | ],
529 | "authors": [
530 | {
531 | "name": "Sebastian Bergmann",
532 | "email": "sebastian@phpunit.de",
533 | "role": "lead"
534 | }
535 | ],
536 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
537 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
538 | "keywords": [
539 | "filesystem",
540 | "iterator"
541 | ],
542 | "time": "2018-09-13T20:33:42+00:00"
543 | },
544 | {
545 | "name": "phpunit/php-text-template",
546 | "version": "1.2.1",
547 | "source": {
548 | "type": "git",
549 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
550 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
551 | },
552 | "dist": {
553 | "type": "zip",
554 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
555 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
556 | "shasum": ""
557 | },
558 | "require": {
559 | "php": ">=5.3.3"
560 | },
561 | "type": "library",
562 | "autoload": {
563 | "classmap": [
564 | "src/"
565 | ]
566 | },
567 | "notification-url": "https://packagist.org/downloads/",
568 | "license": [
569 | "BSD-3-Clause"
570 | ],
571 | "authors": [
572 | {
573 | "name": "Sebastian Bergmann",
574 | "email": "sebastian@phpunit.de",
575 | "role": "lead"
576 | }
577 | ],
578 | "description": "Simple template engine.",
579 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
580 | "keywords": [
581 | "template"
582 | ],
583 | "time": "2015-06-21T13:50:34+00:00"
584 | },
585 | {
586 | "name": "phpunit/php-timer",
587 | "version": "2.1.1",
588 | "source": {
589 | "type": "git",
590 | "url": "https://github.com/sebastianbergmann/php-timer.git",
591 | "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059"
592 | },
593 | "dist": {
594 | "type": "zip",
595 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b389aebe1b8b0578430bda0c7c95a829608e059",
596 | "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059",
597 | "shasum": ""
598 | },
599 | "require": {
600 | "php": "^7.1"
601 | },
602 | "require-dev": {
603 | "phpunit/phpunit": "^7.0"
604 | },
605 | "type": "library",
606 | "extra": {
607 | "branch-alias": {
608 | "dev-master": "2.1-dev"
609 | }
610 | },
611 | "autoload": {
612 | "classmap": [
613 | "src/"
614 | ]
615 | },
616 | "notification-url": "https://packagist.org/downloads/",
617 | "license": [
618 | "BSD-3-Clause"
619 | ],
620 | "authors": [
621 | {
622 | "name": "Sebastian Bergmann",
623 | "email": "sebastian@phpunit.de",
624 | "role": "lead"
625 | }
626 | ],
627 | "description": "Utility class for timing",
628 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
629 | "keywords": [
630 | "timer"
631 | ],
632 | "time": "2019-02-20T10:12:59+00:00"
633 | },
634 | {
635 | "name": "phpunit/php-token-stream",
636 | "version": "3.0.1",
637 | "source": {
638 | "type": "git",
639 | "url": "https://github.com/sebastianbergmann/php-token-stream.git",
640 | "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18"
641 | },
642 | "dist": {
643 | "type": "zip",
644 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18",
645 | "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18",
646 | "shasum": ""
647 | },
648 | "require": {
649 | "ext-tokenizer": "*",
650 | "php": "^7.1"
651 | },
652 | "require-dev": {
653 | "phpunit/phpunit": "^7.0"
654 | },
655 | "type": "library",
656 | "extra": {
657 | "branch-alias": {
658 | "dev-master": "3.0-dev"
659 | }
660 | },
661 | "autoload": {
662 | "classmap": [
663 | "src/"
664 | ]
665 | },
666 | "notification-url": "https://packagist.org/downloads/",
667 | "license": [
668 | "BSD-3-Clause"
669 | ],
670 | "authors": [
671 | {
672 | "name": "Sebastian Bergmann",
673 | "email": "sebastian@phpunit.de"
674 | }
675 | ],
676 | "description": "Wrapper around PHP's tokenizer extension.",
677 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
678 | "keywords": [
679 | "tokenizer"
680 | ],
681 | "time": "2018-10-30T05:52:18+00:00"
682 | },
683 | {
684 | "name": "phpunit/phpunit",
685 | "version": "7.5.7",
686 | "source": {
687 | "type": "git",
688 | "url": "https://github.com/sebastianbergmann/phpunit.git",
689 | "reference": "eb343b86753d26de07ecba7868fa983104361948"
690 | },
691 | "dist": {
692 | "type": "zip",
693 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/eb343b86753d26de07ecba7868fa983104361948",
694 | "reference": "eb343b86753d26de07ecba7868fa983104361948",
695 | "shasum": ""
696 | },
697 | "require": {
698 | "doctrine/instantiator": "^1.1",
699 | "ext-dom": "*",
700 | "ext-json": "*",
701 | "ext-libxml": "*",
702 | "ext-mbstring": "*",
703 | "ext-xml": "*",
704 | "myclabs/deep-copy": "^1.7",
705 | "phar-io/manifest": "^1.0.2",
706 | "phar-io/version": "^2.0",
707 | "php": "^7.1",
708 | "phpspec/prophecy": "^1.7",
709 | "phpunit/php-code-coverage": "^6.0.7",
710 | "phpunit/php-file-iterator": "^2.0.1",
711 | "phpunit/php-text-template": "^1.2.1",
712 | "phpunit/php-timer": "^2.1",
713 | "sebastian/comparator": "^3.0",
714 | "sebastian/diff": "^3.0",
715 | "sebastian/environment": "^4.0",
716 | "sebastian/exporter": "^3.1",
717 | "sebastian/global-state": "^2.0",
718 | "sebastian/object-enumerator": "^3.0.3",
719 | "sebastian/resource-operations": "^2.0",
720 | "sebastian/version": "^2.0.1"
721 | },
722 | "conflict": {
723 | "phpunit/phpunit-mock-objects": "*"
724 | },
725 | "require-dev": {
726 | "ext-pdo": "*"
727 | },
728 | "suggest": {
729 | "ext-soap": "*",
730 | "ext-xdebug": "*",
731 | "phpunit/php-invoker": "^2.0"
732 | },
733 | "bin": [
734 | "phpunit"
735 | ],
736 | "type": "library",
737 | "extra": {
738 | "branch-alias": {
739 | "dev-master": "7.5-dev"
740 | }
741 | },
742 | "autoload": {
743 | "classmap": [
744 | "src/"
745 | ]
746 | },
747 | "notification-url": "https://packagist.org/downloads/",
748 | "license": [
749 | "BSD-3-Clause"
750 | ],
751 | "authors": [
752 | {
753 | "name": "Sebastian Bergmann",
754 | "email": "sebastian@phpunit.de",
755 | "role": "lead"
756 | }
757 | ],
758 | "description": "The PHP Unit Testing framework.",
759 | "homepage": "https://phpunit.de/",
760 | "keywords": [
761 | "phpunit",
762 | "testing",
763 | "xunit"
764 | ],
765 | "time": "2019-03-16T07:31:17+00:00"
766 | },
767 | {
768 | "name": "sebastian/code-unit-reverse-lookup",
769 | "version": "1.0.1",
770 | "source": {
771 | "type": "git",
772 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
773 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
774 | },
775 | "dist": {
776 | "type": "zip",
777 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
778 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
779 | "shasum": ""
780 | },
781 | "require": {
782 | "php": "^5.6 || ^7.0"
783 | },
784 | "require-dev": {
785 | "phpunit/phpunit": "^5.7 || ^6.0"
786 | },
787 | "type": "library",
788 | "extra": {
789 | "branch-alias": {
790 | "dev-master": "1.0.x-dev"
791 | }
792 | },
793 | "autoload": {
794 | "classmap": [
795 | "src/"
796 | ]
797 | },
798 | "notification-url": "https://packagist.org/downloads/",
799 | "license": [
800 | "BSD-3-Clause"
801 | ],
802 | "authors": [
803 | {
804 | "name": "Sebastian Bergmann",
805 | "email": "sebastian@phpunit.de"
806 | }
807 | ],
808 | "description": "Looks up which function or method a line of code belongs to",
809 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
810 | "time": "2017-03-04T06:30:41+00:00"
811 | },
812 | {
813 | "name": "sebastian/comparator",
814 | "version": "3.0.2",
815 | "source": {
816 | "type": "git",
817 | "url": "https://github.com/sebastianbergmann/comparator.git",
818 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
819 | },
820 | "dist": {
821 | "type": "zip",
822 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
823 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
824 | "shasum": ""
825 | },
826 | "require": {
827 | "php": "^7.1",
828 | "sebastian/diff": "^3.0",
829 | "sebastian/exporter": "^3.1"
830 | },
831 | "require-dev": {
832 | "phpunit/phpunit": "^7.1"
833 | },
834 | "type": "library",
835 | "extra": {
836 | "branch-alias": {
837 | "dev-master": "3.0-dev"
838 | }
839 | },
840 | "autoload": {
841 | "classmap": [
842 | "src/"
843 | ]
844 | },
845 | "notification-url": "https://packagist.org/downloads/",
846 | "license": [
847 | "BSD-3-Clause"
848 | ],
849 | "authors": [
850 | {
851 | "name": "Jeff Welch",
852 | "email": "whatthejeff@gmail.com"
853 | },
854 | {
855 | "name": "Volker Dusch",
856 | "email": "github@wallbash.com"
857 | },
858 | {
859 | "name": "Bernhard Schussek",
860 | "email": "bschussek@2bepublished.at"
861 | },
862 | {
863 | "name": "Sebastian Bergmann",
864 | "email": "sebastian@phpunit.de"
865 | }
866 | ],
867 | "description": "Provides the functionality to compare PHP values for equality",
868 | "homepage": "https://github.com/sebastianbergmann/comparator",
869 | "keywords": [
870 | "comparator",
871 | "compare",
872 | "equality"
873 | ],
874 | "time": "2018-07-12T15:12:46+00:00"
875 | },
876 | {
877 | "name": "sebastian/diff",
878 | "version": "3.0.2",
879 | "source": {
880 | "type": "git",
881 | "url": "https://github.com/sebastianbergmann/diff.git",
882 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
883 | },
884 | "dist": {
885 | "type": "zip",
886 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
887 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
888 | "shasum": ""
889 | },
890 | "require": {
891 | "php": "^7.1"
892 | },
893 | "require-dev": {
894 | "phpunit/phpunit": "^7.5 || ^8.0",
895 | "symfony/process": "^2 || ^3.3 || ^4"
896 | },
897 | "type": "library",
898 | "extra": {
899 | "branch-alias": {
900 | "dev-master": "3.0-dev"
901 | }
902 | },
903 | "autoload": {
904 | "classmap": [
905 | "src/"
906 | ]
907 | },
908 | "notification-url": "https://packagist.org/downloads/",
909 | "license": [
910 | "BSD-3-Clause"
911 | ],
912 | "authors": [
913 | {
914 | "name": "Kore Nordmann",
915 | "email": "mail@kore-nordmann.de"
916 | },
917 | {
918 | "name": "Sebastian Bergmann",
919 | "email": "sebastian@phpunit.de"
920 | }
921 | ],
922 | "description": "Diff implementation",
923 | "homepage": "https://github.com/sebastianbergmann/diff",
924 | "keywords": [
925 | "diff",
926 | "udiff",
927 | "unidiff",
928 | "unified diff"
929 | ],
930 | "time": "2019-02-04T06:01:07+00:00"
931 | },
932 | {
933 | "name": "sebastian/environment",
934 | "version": "4.1.0",
935 | "source": {
936 | "type": "git",
937 | "url": "https://github.com/sebastianbergmann/environment.git",
938 | "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656"
939 | },
940 | "dist": {
941 | "type": "zip",
942 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fda8ce1974b62b14935adc02a9ed38252eca656",
943 | "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656",
944 | "shasum": ""
945 | },
946 | "require": {
947 | "php": "^7.1"
948 | },
949 | "require-dev": {
950 | "phpunit/phpunit": "^7.5"
951 | },
952 | "suggest": {
953 | "ext-posix": "*"
954 | },
955 | "type": "library",
956 | "extra": {
957 | "branch-alias": {
958 | "dev-master": "4.1-dev"
959 | }
960 | },
961 | "autoload": {
962 | "classmap": [
963 | "src/"
964 | ]
965 | },
966 | "notification-url": "https://packagist.org/downloads/",
967 | "license": [
968 | "BSD-3-Clause"
969 | ],
970 | "authors": [
971 | {
972 | "name": "Sebastian Bergmann",
973 | "email": "sebastian@phpunit.de"
974 | }
975 | ],
976 | "description": "Provides functionality to handle HHVM/PHP environments",
977 | "homepage": "http://www.github.com/sebastianbergmann/environment",
978 | "keywords": [
979 | "Xdebug",
980 | "environment",
981 | "hhvm"
982 | ],
983 | "time": "2019-02-01T05:27:49+00:00"
984 | },
985 | {
986 | "name": "sebastian/exporter",
987 | "version": "3.1.0",
988 | "source": {
989 | "type": "git",
990 | "url": "https://github.com/sebastianbergmann/exporter.git",
991 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
992 | },
993 | "dist": {
994 | "type": "zip",
995 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
996 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
997 | "shasum": ""
998 | },
999 | "require": {
1000 | "php": "^7.0",
1001 | "sebastian/recursion-context": "^3.0"
1002 | },
1003 | "require-dev": {
1004 | "ext-mbstring": "*",
1005 | "phpunit/phpunit": "^6.0"
1006 | },
1007 | "type": "library",
1008 | "extra": {
1009 | "branch-alias": {
1010 | "dev-master": "3.1.x-dev"
1011 | }
1012 | },
1013 | "autoload": {
1014 | "classmap": [
1015 | "src/"
1016 | ]
1017 | },
1018 | "notification-url": "https://packagist.org/downloads/",
1019 | "license": [
1020 | "BSD-3-Clause"
1021 | ],
1022 | "authors": [
1023 | {
1024 | "name": "Jeff Welch",
1025 | "email": "whatthejeff@gmail.com"
1026 | },
1027 | {
1028 | "name": "Volker Dusch",
1029 | "email": "github@wallbash.com"
1030 | },
1031 | {
1032 | "name": "Bernhard Schussek",
1033 | "email": "bschussek@2bepublished.at"
1034 | },
1035 | {
1036 | "name": "Sebastian Bergmann",
1037 | "email": "sebastian@phpunit.de"
1038 | },
1039 | {
1040 | "name": "Adam Harvey",
1041 | "email": "aharvey@php.net"
1042 | }
1043 | ],
1044 | "description": "Provides the functionality to export PHP variables for visualization",
1045 | "homepage": "http://www.github.com/sebastianbergmann/exporter",
1046 | "keywords": [
1047 | "export",
1048 | "exporter"
1049 | ],
1050 | "time": "2017-04-03T13:19:02+00:00"
1051 | },
1052 | {
1053 | "name": "sebastian/global-state",
1054 | "version": "2.0.0",
1055 | "source": {
1056 | "type": "git",
1057 | "url": "https://github.com/sebastianbergmann/global-state.git",
1058 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4"
1059 | },
1060 | "dist": {
1061 | "type": "zip",
1062 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
1063 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
1064 | "shasum": ""
1065 | },
1066 | "require": {
1067 | "php": "^7.0"
1068 | },
1069 | "require-dev": {
1070 | "phpunit/phpunit": "^6.0"
1071 | },
1072 | "suggest": {
1073 | "ext-uopz": "*"
1074 | },
1075 | "type": "library",
1076 | "extra": {
1077 | "branch-alias": {
1078 | "dev-master": "2.0-dev"
1079 | }
1080 | },
1081 | "autoload": {
1082 | "classmap": [
1083 | "src/"
1084 | ]
1085 | },
1086 | "notification-url": "https://packagist.org/downloads/",
1087 | "license": [
1088 | "BSD-3-Clause"
1089 | ],
1090 | "authors": [
1091 | {
1092 | "name": "Sebastian Bergmann",
1093 | "email": "sebastian@phpunit.de"
1094 | }
1095 | ],
1096 | "description": "Snapshotting of global state",
1097 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
1098 | "keywords": [
1099 | "global state"
1100 | ],
1101 | "time": "2017-04-27T15:39:26+00:00"
1102 | },
1103 | {
1104 | "name": "sebastian/object-enumerator",
1105 | "version": "3.0.3",
1106 | "source": {
1107 | "type": "git",
1108 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1109 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
1110 | },
1111 | "dist": {
1112 | "type": "zip",
1113 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
1114 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
1115 | "shasum": ""
1116 | },
1117 | "require": {
1118 | "php": "^7.0",
1119 | "sebastian/object-reflector": "^1.1.1",
1120 | "sebastian/recursion-context": "^3.0"
1121 | },
1122 | "require-dev": {
1123 | "phpunit/phpunit": "^6.0"
1124 | },
1125 | "type": "library",
1126 | "extra": {
1127 | "branch-alias": {
1128 | "dev-master": "3.0.x-dev"
1129 | }
1130 | },
1131 | "autoload": {
1132 | "classmap": [
1133 | "src/"
1134 | ]
1135 | },
1136 | "notification-url": "https://packagist.org/downloads/",
1137 | "license": [
1138 | "BSD-3-Clause"
1139 | ],
1140 | "authors": [
1141 | {
1142 | "name": "Sebastian Bergmann",
1143 | "email": "sebastian@phpunit.de"
1144 | }
1145 | ],
1146 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1147 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1148 | "time": "2017-08-03T12:35:26+00:00"
1149 | },
1150 | {
1151 | "name": "sebastian/object-reflector",
1152 | "version": "1.1.1",
1153 | "source": {
1154 | "type": "git",
1155 | "url": "https://github.com/sebastianbergmann/object-reflector.git",
1156 | "reference": "773f97c67f28de00d397be301821b06708fca0be"
1157 | },
1158 | "dist": {
1159 | "type": "zip",
1160 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
1161 | "reference": "773f97c67f28de00d397be301821b06708fca0be",
1162 | "shasum": ""
1163 | },
1164 | "require": {
1165 | "php": "^7.0"
1166 | },
1167 | "require-dev": {
1168 | "phpunit/phpunit": "^6.0"
1169 | },
1170 | "type": "library",
1171 | "extra": {
1172 | "branch-alias": {
1173 | "dev-master": "1.1-dev"
1174 | }
1175 | },
1176 | "autoload": {
1177 | "classmap": [
1178 | "src/"
1179 | ]
1180 | },
1181 | "notification-url": "https://packagist.org/downloads/",
1182 | "license": [
1183 | "BSD-3-Clause"
1184 | ],
1185 | "authors": [
1186 | {
1187 | "name": "Sebastian Bergmann",
1188 | "email": "sebastian@phpunit.de"
1189 | }
1190 | ],
1191 | "description": "Allows reflection of object attributes, including inherited and non-public ones",
1192 | "homepage": "https://github.com/sebastianbergmann/object-reflector/",
1193 | "time": "2017-03-29T09:07:27+00:00"
1194 | },
1195 | {
1196 | "name": "sebastian/recursion-context",
1197 | "version": "3.0.0",
1198 | "source": {
1199 | "type": "git",
1200 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1201 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
1202 | },
1203 | "dist": {
1204 | "type": "zip",
1205 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
1206 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
1207 | "shasum": ""
1208 | },
1209 | "require": {
1210 | "php": "^7.0"
1211 | },
1212 | "require-dev": {
1213 | "phpunit/phpunit": "^6.0"
1214 | },
1215 | "type": "library",
1216 | "extra": {
1217 | "branch-alias": {
1218 | "dev-master": "3.0.x-dev"
1219 | }
1220 | },
1221 | "autoload": {
1222 | "classmap": [
1223 | "src/"
1224 | ]
1225 | },
1226 | "notification-url": "https://packagist.org/downloads/",
1227 | "license": [
1228 | "BSD-3-Clause"
1229 | ],
1230 | "authors": [
1231 | {
1232 | "name": "Jeff Welch",
1233 | "email": "whatthejeff@gmail.com"
1234 | },
1235 | {
1236 | "name": "Sebastian Bergmann",
1237 | "email": "sebastian@phpunit.de"
1238 | },
1239 | {
1240 | "name": "Adam Harvey",
1241 | "email": "aharvey@php.net"
1242 | }
1243 | ],
1244 | "description": "Provides functionality to recursively process PHP variables",
1245 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1246 | "time": "2017-03-03T06:23:57+00:00"
1247 | },
1248 | {
1249 | "name": "sebastian/resource-operations",
1250 | "version": "2.0.1",
1251 | "source": {
1252 | "type": "git",
1253 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
1254 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9"
1255 | },
1256 | "dist": {
1257 | "type": "zip",
1258 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
1259 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
1260 | "shasum": ""
1261 | },
1262 | "require": {
1263 | "php": "^7.1"
1264 | },
1265 | "type": "library",
1266 | "extra": {
1267 | "branch-alias": {
1268 | "dev-master": "2.0-dev"
1269 | }
1270 | },
1271 | "autoload": {
1272 | "classmap": [
1273 | "src/"
1274 | ]
1275 | },
1276 | "notification-url": "https://packagist.org/downloads/",
1277 | "license": [
1278 | "BSD-3-Clause"
1279 | ],
1280 | "authors": [
1281 | {
1282 | "name": "Sebastian Bergmann",
1283 | "email": "sebastian@phpunit.de"
1284 | }
1285 | ],
1286 | "description": "Provides a list of PHP built-in functions that operate on resources",
1287 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1288 | "time": "2018-10-04T04:07:39+00:00"
1289 | },
1290 | {
1291 | "name": "sebastian/version",
1292 | "version": "2.0.1",
1293 | "source": {
1294 | "type": "git",
1295 | "url": "https://github.com/sebastianbergmann/version.git",
1296 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
1297 | },
1298 | "dist": {
1299 | "type": "zip",
1300 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
1301 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
1302 | "shasum": ""
1303 | },
1304 | "require": {
1305 | "php": ">=5.6"
1306 | },
1307 | "type": "library",
1308 | "extra": {
1309 | "branch-alias": {
1310 | "dev-master": "2.0.x-dev"
1311 | }
1312 | },
1313 | "autoload": {
1314 | "classmap": [
1315 | "src/"
1316 | ]
1317 | },
1318 | "notification-url": "https://packagist.org/downloads/",
1319 | "license": [
1320 | "BSD-3-Clause"
1321 | ],
1322 | "authors": [
1323 | {
1324 | "name": "Sebastian Bergmann",
1325 | "email": "sebastian@phpunit.de",
1326 | "role": "lead"
1327 | }
1328 | ],
1329 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1330 | "homepage": "https://github.com/sebastianbergmann/version",
1331 | "time": "2016-10-03T07:35:21+00:00"
1332 | },
1333 | {
1334 | "name": "symfony/polyfill-ctype",
1335 | "version": "v1.11.0",
1336 | "source": {
1337 | "type": "git",
1338 | "url": "https://github.com/symfony/polyfill-ctype.git",
1339 | "reference": "82ebae02209c21113908c229e9883c419720738a"
1340 | },
1341 | "dist": {
1342 | "type": "zip",
1343 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
1344 | "reference": "82ebae02209c21113908c229e9883c419720738a",
1345 | "shasum": ""
1346 | },
1347 | "require": {
1348 | "php": ">=5.3.3"
1349 | },
1350 | "suggest": {
1351 | "ext-ctype": "For best performance"
1352 | },
1353 | "type": "library",
1354 | "extra": {
1355 | "branch-alias": {
1356 | "dev-master": "1.11-dev"
1357 | }
1358 | },
1359 | "autoload": {
1360 | "psr-4": {
1361 | "Symfony\\Polyfill\\Ctype\\": ""
1362 | },
1363 | "files": [
1364 | "bootstrap.php"
1365 | ]
1366 | },
1367 | "notification-url": "https://packagist.org/downloads/",
1368 | "license": [
1369 | "MIT"
1370 | ],
1371 | "authors": [
1372 | {
1373 | "name": "Symfony Community",
1374 | "homepage": "https://symfony.com/contributors"
1375 | },
1376 | {
1377 | "name": "Gert de Pagter",
1378 | "email": "BackEndTea@gmail.com"
1379 | }
1380 | ],
1381 | "description": "Symfony polyfill for ctype functions",
1382 | "homepage": "https://symfony.com",
1383 | "keywords": [
1384 | "compatibility",
1385 | "ctype",
1386 | "polyfill",
1387 | "portable"
1388 | ],
1389 | "time": "2019-02-06T07:57:58+00:00"
1390 | },
1391 | {
1392 | "name": "theseer/tokenizer",
1393 | "version": "1.1.0",
1394 | "source": {
1395 | "type": "git",
1396 | "url": "https://github.com/theseer/tokenizer.git",
1397 | "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b"
1398 | },
1399 | "dist": {
1400 | "type": "zip",
1401 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b",
1402 | "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b",
1403 | "shasum": ""
1404 | },
1405 | "require": {
1406 | "ext-dom": "*",
1407 | "ext-tokenizer": "*",
1408 | "ext-xmlwriter": "*",
1409 | "php": "^7.0"
1410 | },
1411 | "type": "library",
1412 | "autoload": {
1413 | "classmap": [
1414 | "src/"
1415 | ]
1416 | },
1417 | "notification-url": "https://packagist.org/downloads/",
1418 | "license": [
1419 | "BSD-3-Clause"
1420 | ],
1421 | "authors": [
1422 | {
1423 | "name": "Arne Blankerts",
1424 | "email": "arne@blankerts.de",
1425 | "role": "Developer"
1426 | }
1427 | ],
1428 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
1429 | "time": "2017-04-07T12:08:54+00:00"
1430 | },
1431 | {
1432 | "name": "webmozart/assert",
1433 | "version": "1.4.0",
1434 | "source": {
1435 | "type": "git",
1436 | "url": "https://github.com/webmozart/assert.git",
1437 | "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
1438 | },
1439 | "dist": {
1440 | "type": "zip",
1441 | "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
1442 | "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
1443 | "shasum": ""
1444 | },
1445 | "require": {
1446 | "php": "^5.3.3 || ^7.0",
1447 | "symfony/polyfill-ctype": "^1.8"
1448 | },
1449 | "require-dev": {
1450 | "phpunit/phpunit": "^4.6",
1451 | "sebastian/version": "^1.0.1"
1452 | },
1453 | "type": "library",
1454 | "extra": {
1455 | "branch-alias": {
1456 | "dev-master": "1.3-dev"
1457 | }
1458 | },
1459 | "autoload": {
1460 | "psr-4": {
1461 | "Webmozart\\Assert\\": "src/"
1462 | }
1463 | },
1464 | "notification-url": "https://packagist.org/downloads/",
1465 | "license": [
1466 | "MIT"
1467 | ],
1468 | "authors": [
1469 | {
1470 | "name": "Bernhard Schussek",
1471 | "email": "bschussek@gmail.com"
1472 | }
1473 | ],
1474 | "description": "Assertions to validate method input/output with nice error messages.",
1475 | "keywords": [
1476 | "assert",
1477 | "check",
1478 | "validate"
1479 | ],
1480 | "time": "2018-12-25T11:19:39+00:00"
1481 | }
1482 | ],
1483 | "aliases": [],
1484 | "minimum-stability": "stable",
1485 | "stability-flags": [],
1486 | "prefer-stable": false,
1487 | "prefer-lowest": false,
1488 | "platform": [],
1489 | "platform-dev": []
1490 | }
1491 |
--------------------------------------------------------------------------------