Compare commits

...

No commits in common. "1.2.0-RC1" and "master" have entirely different histories.

57 changed files with 2933 additions and 1930 deletions

3
.gitattributes vendored
View File

@ -1,4 +1,5 @@
.gitattributes export-ignore
.gitignore export-ignore
.gitlab-ci.yml export-ignore
test/ export-ignore
tests/ export-ignore
CI/ export-ignore

4
.gitignore vendored
View File

@ -1,6 +1,6 @@
composer.lock
composer.phar
composer.lock
.idea/
build/
vendor/
test/temp/
vendor/

View File

@ -1,82 +0,0 @@
before_script:
# Install dependencies
- set -xe
- apt-get update -yqq
- apt-get install git zip unzip -yqq
stages:
- build
- test
- deploy
build:composer:
image: php:7.2
stage: build
script:
- curl -sS https://getcomposer.org/installer | php
- php composer.phar install
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/
test:7.1:
stage: test
image: php:7.1
script:
- vendor/bin/phpunit -c test/phpunit.xml
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor
test:7.2:
stage: test
image: php:7.2
script:
- vendor/bin/phpunit -c test/phpunit.xml
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/
test:7.3:
stage: test
image: php:7.3
script:
- vendor/bin/phpunit -c test/phpunit.xml
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/
test:coverage:
stage: test
image: php:7.2
script:
- pecl install xdebug
- docker-php-ext-enable xdebug
- vendor/bin/phpunit -c test/phpunit.xml --coverage-text
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/
release:
stage: deploy
image: php:7.2
only:
- master
script:
- pecl install xdebug
- docker-php-ext-enable xdebug
- vendor/bin/phpunit -c test/phpunit.xml --coverage-text
artifacts:
name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}"
paths:
- build/
expire_in: 3 weeks
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/

View File

@ -1,12 +0,0 @@
language: php
php:
- 7.1
- 7.2
- 7.3
script:
- php vendor/bin/phpunit -v -c test/phpunit.xml --coverage-text
before_script:
- composer install

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2013-2018 TechFuze
Copyright (c) 2013-2021 TechFuze
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,59 +0,0 @@
FuzeWorks::Layout Component - Readme
===================
Version 1.2.0
A versatile PHP Framework built to perform.
https://techfuze.net/fuzeworks
Summary
-------
The FuzeWorks Layout Component provides a wrapper for multiple template engines
into FuzeWorks. This allows the developer to easily switch between different engines and
integrate these into FuzeWorks projects. This system consists of a ``Component`` and thus should
be loaded before creating the FuzeWorks ``Container``.
Copyright
---------
Copyright © 2013 onwards -- TechFuze
Certain libraries are copyrighted by their respective authors;
see the full copyright list for details.
License
-------
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Licensing of current contributions
----------------------------------
Beginning on 2018-04-17, new contributions to this codebase are all licensed
under terms compatible with the MIT license. FuzeWorks is currently
transitioning older code to the MIT License, but work is not yet complete.
Enjoy!
------
TechFuze

View File

@ -1,36 +1,34 @@
{
"name": "fuzeworks/layout",
"description": "FuzeWorks Framework Layout Template System",
"name": "fuzeworks/database",
"description": "FuzeWorks Framework Database Component",
"homepage": "https://i15.nl/fuzeworks",
"license": ["MIT"],
"authors": [
{
"name": "TechFuze",
"homepage": "https://techfuze.net"
},
{
"name": "FuzeWorks Community",
"homepage": "https://techfuze.net/fuzeworks/contributors"
"name": "Abel Hoogeveen",
"homepage": "https://i15.nl"
}
],
"require": {
"php": ">=7.1.0",
"fuzeworks/core": "1.2.0-RC2"
"php": ">=8.1.0",
"fuzeworks/core": "~1.3.0",
"fuzeworks/mvcr": "~1.3.0"
},
"require-dev": {
"ext-json": "*",
"smarty/smarty": "~3.1",
"latte/latte": "~2.4",
"phpunit/phpunit": "^7",
"mikey179/vfsStream": "1.6.5"
"phpunit/phpunit": "^9",
"fuzeworks/tracycomponent": "~1.3.0",
"mongodb/mongodb": "~1.15.0",
"ext-pdo": "*",
"ext-mongodb": "*"
},
"suggest": {
"smarty/smarty": "Template Engine that is natively supported by FuzeWorks.",
"latte/latte": "Template Engine that is natively supported by FuzeWorks"
"mongodb/mongodb": "For using MongoDB databases with this component",
"ext-pdo": "For using PDO endpoints with this component",
"ext-mongodb": "For using MongoDB endpoints with this component"
},
"autoload": {
"psr-4": {
"FuzeWorks\\": "src/FuzeWorks/"
}
}
}
}

View File

@ -0,0 +1,54 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
return [
'active_group' => 'default',
'connections' => [
'default' => [
'engineName' => 'pdo',
'dsn' => '',
'hostname' => '',
'username' => '',
'password' => '',
'database' => '',
'prefix' => '',
'persistent' => false,
'debug' => false,
'charset' => 'utf8',
'collation' => 'utf8_general_ci'
]
]
];

362
src/FuzeWorks/Database.php Normal file
View File

@ -0,0 +1,362 @@
<?php
/**
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.1.4
*
* @version Version 1.1.4
*/
namespace FuzeWorks;
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
use FuzeWorks\DatabaseEngine\MongoEngine;
use FuzeWorks\DatabaseEngine\PDOEngine;
use FuzeWorks\Event\DatabaseLoadDriverEvent;
use FuzeWorks\Event\DatabaseLoadTableModelEvent;
use FuzeWorks\Exception\DatabaseException;
use FuzeWorks\Exception\EventException;
use FuzeWorks\Model\iDatabaseTableModel;
use FuzeWorks\Model\MongoTableModel;
use FuzeWorks\Model\PDOTableModel;
/**
* Database loading class
*
* Loads databases, forges and utilities in a standardized manner.
*
* @author TechFuze <contact@techfuze.net>
* @copyright (c) 2013 - 2014, TechFuze. (https://techfuze.net)
*
*/
class Database
{
/**
* Current database config as found the database config file with highest priority
*
* @var array
*/
protected array $dbConfig;
/**
* All engines that can be used for databases
*
* @var iDatabaseEngine[]
*/
protected array $engines = [];
/**
* All tableModels that can be used for connections
*
* @var iDatabaseTableModel[]
*/
protected array $tableModels = [];
/**
* Whether all DatabaseEngines have been loaded yet
*
* @var bool
*/
protected bool $enginesLoaded = false;
/**
* Array of all the database engines
*
* @var iDatabaseEngine[]
*/
protected array $connections = [];
/**
* Array of all the tableModels
*
* @var iDatabaseTableModel[]
*/
protected array $tables;
/**
* Register with the TracyBridge upon startup
*/
public function init(): void
{
$this->dbConfig = Factory::getInstance()->config->get('database')->toArray();
if (class_exists('Tracy\Debugger'))
DatabaseTracyBridge::register();
}
/**
* Close connections when shutting down FuzeWorks
*/
public function __destruct()
{
foreach ($this->connections as $connection)
$connection->tearDown();
}
/**
*
* When providing a database using the databaseLoadDriverEvent, parameters and connectionName
* will be ignored.
*
* @param string $connectionName
* @param string $engineName
* @param array $parameters
* @return iDatabaseEngine
* @throws DatabaseException
*/
public function get(string $connectionName = 'default', string $engineName = '', array $parameters = []): iDatabaseEngine
{
// Fire the event to allow settings to be changed
/** @var DatabaseLoadDriverEvent $event */
try {
$event = Events::fireEvent('databaseLoadDriverEvent', strtolower($engineName), $parameters, $connectionName);
} catch (EventException $e) {
throw new DatabaseException("Could not get database. databaseLoadDriverEvent threw exception: '".$e->getMessage()."'");
}
if ($event->isCancelled())
throw new DatabaseException("Could not get database. Cancelled by databaseLoadDriverEvent.");
/** @var iDatabaseEngine $engine */
// If a databaseEngine is provided by the event, use that. Otherwise search in the list of engines
if ($event->databaseEngine instanceof iDatabaseEngine)
{
// Do intervention first
$engine = $this->connections[$event->connectionName] = $event->databaseEngine;
if (!$engine->isSetup())
$engine->setUp($event->parameters);
}
elseif (isset($this->connections[$event->connectionName]))
{
// Do already exists second
$engine = $this->connections[$event->connectionName];
}
elseif (!empty($event->engineName) && !empty($event->parameters))
{
// Do provided config third
$engineClass = get_class($this->fetchEngine($event->engineName));
$engine = $this->connections[$event->connectionName] = new $engineClass();
$engine->setUp($event->parameters);
}
else
{
// Do external config fourth
if (!isset($this->dbConfig['connections'][$event->connectionName]))
throw new DatabaseException("Could not get database. Database not found in config.");
$engineName = $this->dbConfig['connections'][$event->connectionName]['engineName'];
$engineClass = get_class($this->fetchEngine($engineName));
$engine = $this->connections[$event->connectionName] = new $engineClass();
$engine->setUp($this->dbConfig['connections'][$event->connectionName]);
}
// Tie it into the Tracy Bar if available
if (class_exists('\Tracy\Debugger'))
DatabaseTracyBridge::registerDatabase($engine);
return $engine;
}
/**
* @param string $tableName
* @param string $connectionName
* @param string $engineName
* @param array $parameters
* @return iDatabaseTableModel
* @throws DatabaseException
*/
public function getTableModel(string $tableName, string $connectionName = 'default', string $engineName = '', array $parameters = []): iDatabaseTableModel
{
try {
/** @var DatabaseLoadTableModelEvent $event */
$event = Events::fireEvent('databaseLoadTableModelEvent', strtolower($engineName), $parameters, $connectionName, $tableName);
} catch (EventException $e) {
throw new DatabaseException("Could not get TableModel. databaseLoadTableModelEvent threw exception: '" . $e->getMessage() . "'");
}
if ($event->isCancelled())
throw new DatabaseException("Could not get TableModel. Cancelled by databaseLoadTableModelEvent.");
/** @var iDatabaseTableModel $tableModel */
// If a TableModel is provided by the event, use that. Otherwise search in the list of tableModels
if ($event->tableModel instanceof iDatabaseTableModel)
{
$tableModel = $this->tables[$event->connectionName . "|" . $event->tableName] = $event->tableModel;
if (!$tableModel->isSetup())
$tableModel->setUp($this->get($event->connectionName, $tableModel->getEngineName(), $event->parameters), $event->tableName);
}
// If the connection already exists, use that
elseif (isset($this->tables[$event->connectionName . "|" . $event->tableName]))
{
$tableModel = $this->tables[$event->connectionName . "|" . $event->tableName];
}
// Otherwise use the provided configuration
else
{
// First the engine shall be fetched, so the name of the tableModel is known
$engine = $this->get($event->connectionName, $event->engineName, $event->parameters);
$tableModelClass = get_class($this->fetchTableModel($engine->getName()));
// Load the tableModel and add the engine
$tableModel = $this->tables[$event->connectionName . "|" . $event->tableName] = new $tableModelClass();
$tableModel->setUp($engine, $event->tableName);
}
// And return the tableModel
return $tableModel;
}
/**
* Get a loaded database engine.
*
* @param string $engineName
* @return iDatabaseEngine
* @throws DatabaseException
*/
public function fetchEngine(string $engineName): iDatabaseEngine
{
// First retrieve the name
$engineName = strtolower($engineName);
// Then load all engines
$this->loadDatabaseComponents();
// If the engine exists, return it
if (isset($this->engines[$engineName]))
return $this->engines[$engineName];
// Otherwise throw exception
throw new DatabaseException("Could not get engine. Engine does not exist.");
}
/**
* Fetch a loaded TableModel
*
* @param string $tableModelName
* @return iDatabaseTableModel
* @throws DatabaseException
*/
public function fetchTableModel(string $tableModelName): iDatabaseTableModel
{
// First retrieve the name
$tableModelName = strtolower($tableModelName);
// Then load all the tableModels
$this->loadDatabaseComponents();
// If the tableModel exists, return it
if (isset($this->tableModels[$tableModelName]))
return $this->tableModels[$tableModelName];
// Otherwise throw an exception
throw new DatabaseException("Could not get tableModel. TableModel does not exist.");
}
/**
* Register a new database engine
*
* @param iDatabaseEngine $databaseEngine
* @return bool
* @throws DatabaseException
*/
public function registerEngine(iDatabaseEngine $databaseEngine): bool
{
// First retrieve the name
$engineName = strtolower($databaseEngine->getName());
// Check if the engine is already set
if (isset($this->engines[$engineName]))
throw new DatabaseException("Could not register engine. Engine '".$engineName."' already registered.");
// Install it
$this->engines[$engineName] = $databaseEngine;
Logger::log("Registered Database Engine: '" . $engineName . "'");
return true;
}
/**
* Register a new database tableModel
*
* @param iDatabaseTableModel $tableModel
* @return bool
* @throws DatabaseException
*/
public function registerTableModel(iDatabaseTableModel $tableModel): bool
{
// First retrieve the name
$tableModelName = strtolower($tableModel->getName());
// Check if the tableModel is already set
if (isset($this->tableModels[$tableModelName]))
throw new DatabaseException("Could not register tableModel. TableModel '" . $tableModelName . "' already registered.");
// Install it
$this->tableModels[$tableModelName] = $tableModel;
Logger::log("Registered TableModel type: '" . $tableModelName . "'");
return true;
}
/**
* Load all databaseEngines by firing a databaseLoadEngineEvent and by loading all the default engines
*
* @return bool
* @throws DatabaseException
*/
protected function loadDatabaseComponents(): bool
{
// If already loaded, skip
if ($this->enginesLoaded)
return false;
// Fire engine event
try {
Events::fireEvent('databaseLoadEngineEvent');
} catch (EventException $e) {
throw new DatabaseException("Could not load database engines. databaseLoadEngineEvent threw exception: '" . $e->getMessage() . "'");
}
// Load the engines provided by the DatabaseComponent
$this->registerEngine(new PDOEngine());
$this->registerEngine(new MongoEngine());
// Load the tableModels provided by the DatabaseComponent
$this->registerTableModel(new PDOTableModel());
$this->registerTableModel(new MongoTableModel());
// And save results
$this->enginesLoaded = true;
return true;
}
}

View File

@ -1,6 +1,6 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
@ -29,39 +29,44 @@
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
* @since Version 1.1.4
*
* @version Version 1.2.0
* @version Version 1.1.4
*/
namespace FuzeWorks;
/**
* Class LayoutComponent
* Class DatabaseComponent
*
* Used to register the layout component with the FuzeWorks core.
* Used to register the database component with the FuzeWorks core.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class LayoutComponent implements iComponent
class DatabaseComponent implements iComponent
{
public function getName(): string
{
return 'LayoutComponent';
return "DatabaseComponent";
}
public function getClasses(): array
{
return [
'layouts' => '\FuzeWorks\Layout',
'languages' => '\FuzeWorks\Languages'
'databases' => '\FuzeWorks\Database'
];
}
public function onAddComponent(Configurator $configurator)
{
// Add fallback database config
$configurator->addDirectory(
dirname(__DIR__) . DIRECTORY_SEPARATOR . 'Config',
'config',
Priority::LOWEST
);
}
public function onCreateContainer(Factory $container)

View File

@ -1,10 +1,10 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -25,51 +25,61 @@
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\TemplateEngine;
namespace FuzeWorks\DatabaseEngine;
/**
* Interface that all Template Engines must follow.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* Class DatabaseDriver
*/
interface TemplateEngine
abstract class DatabaseDriver implements iDatabaseEngine
{
/**
* Set the directory of the current template.
*
* @param string $directory Template Directory
*/
public function setDirectory($directory);
// --- Query Logging --------------------------------------------------
/**
* Handle and retrieve a template file.
* All queries performed by this engine
*
* @param string $file Template File
* @param array $assigned_variables All the variables used in this layout
*
* @return string Output of the template
* @var array
*/
public function get($file, $assigned_variables);
private array $queries = [];
/**
* Retrieve the file extensions that this template engine uses.
* Log information about a query. Used for debugging issues
*
* @return array All used extensions. eg: array('php')
* @param string $queryString
* @param int $queryData
* @param float $queryTimings
* @param array $queryError
*/
public function getFileExtensions(): array;
protected function logQuery(string $queryString, int $queryData, float $queryTimings, array $queryError = [])
{
$this->queries[] = [
'queryString' => $queryString,
'queryData' => $queryData,
'queryTimings' => $queryTimings,
'queryError' => $queryError
];
}
/**
* Reset the template engine to its default state, so it can be used again clean.
* Get all performed queries and logged data
*
* @return array
*/
public function reset(): bool;
}
public function getQueries(): array
{
return $this->queries;
}
// --------------------------------------------------------------------
}

View File

@ -0,0 +1,201 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\DatabaseEngine;
use InvalidArgumentException;
use MongoDB\Driver\Monitoring\CommandFailedEvent;
use MongoDB\Driver\Monitoring\CommandStartedEvent;
use MongoDB\Driver\Monitoring\CommandSubscriber;
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
class MongoCommandSubscriber implements CommandSubscriber
{
/**
* @var MongoEngine
*/
private MongoEngine $mongoEngine;
/**
* @var float
*/
private float $commandTimings = 0.0;
/**
* @var string
*/
private string $queryString;
public function __construct(MongoEngine $engine)
{
$this->mongoEngine = $engine;
}
/**
* Notification method for a failed command.
* If the subscriber has been registered with MongoDB\Driver\Monitoring\addSubscriber(), the driver will call this method when a command has failed.
* @link https://secure.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php
* @param CommandFailedEvent $event An event object encapsulating information about the failed command.
* @return void
* @throws InvalidArgumentException on argument parsing errors.
* @since 1.3.0
*/
public function commandFailed(CommandFailedEvent $event): void
{
// TODO: Implement commandFailed() method.
}
/**
* Notification method for a started command.
* If the subscriber has been registered with MongoDB\Driver\Monitoring\addSubscriber(), the driver will call this method when a command has started.
* @link https://secure.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php
* @param CommandStartedEvent $event An event object encapsulating information about the started command.
* @return void
* @throws InvalidArgumentException on argument parsing errors.
* @since 1.3.0
*/
public function commandStarted(CommandStartedEvent $event): void
{
$this->commandTimings = microtime(true);
$this->queryString = strtoupper($event->getCommandName());
// Determine query string
$command = $event->getCommand();
$this->queryString .= ' \'' . $event->getDatabaseName() . '.' . $command->{$event->getCommandName()} . '\'';
// If a projection is provided, print it
if (isset($command->projection))
{
$projection = $command->projection;
$projectionStrings = [];
foreach ($projection as $projectionKey => $projectionVal)
$projectionStrings[] = $projectionKey;
$this->queryString .= " PROJECT[" . implode(',', $projectionStrings) . ']';
}
// If a filter is provided, print it
if (isset($command->filter) && !empty((array) $command->filter))
{
$filter = $command->filter;
$filterStrings = [];
foreach ($filter as $filterKey => $filterVal)
$filterStrings[] = $filterKey;
$this->queryString .= " FILTER[" . implode(',', $filterStrings) . ']';
}
// If a sort is provided, print it
if (isset($command->sort))
{
$sort = $command->sort;
$sortStrings = [];
foreach ($sort as $sortKey => $sortVal)
$sortStrings[] = $sortKey . ($sortVal == 1 ? ' ASC' : ' DESC');
$this->queryString .= " SORT[" . implode(',', $sortStrings) . ']';
}
// If documents are provided, print it
if (isset($command->documents))
{
$documents = $command->documents;
$documentKeys = [];
foreach ($documents as $document)
$documentKeys = array_merge($documentKeys, array_keys((array) $document));
$this->queryString .= " VALUES[" . implode(',', $documentKeys) . ']';
}
// If a deletes is provided, print it
if (isset($command->deletes))
{
$deletes = $command->deletes;
$deleteKeys = [];
foreach ($deletes as $delete)
{
if (!isset($delete->q))
continue;
$deleteKeys = array_merge($deleteKeys, array_keys((array) $delete->q));
}
$this->queryString .= " FILTER[" . implode(',', $deleteKeys) . ']';
}
// If a limit is provided, print it
if (isset($command->limit))
$this->queryString .= " LIMIT(".$command->limit.")";
}
/**
* Notification method for a successful command.
* If the subscriber has been registered with MongoDB\Driver\Monitoring\addSubscriber(), the driver will call this method when a command has succeeded.
* @link https://secure.php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php
* @param CommandSucceededEvent $event An event object encapsulating information about the successful command.
* @return void
* @throws InvalidArgumentException on argument parsing errors.
* @since 1.3.0
*/
public function commandSucceeded(CommandSucceededEvent $event): void
{
// Get variables
$queryTimings = microtime(true) - $this->commandTimings;
$queryString = $this->queryString;
$queryData = 0;
switch ($event->getCommandName())
{
case 'find':
$queryData = count($event->getReply()->cursor->firstBatch);
break;
case 'update':
case 'delete':
case 'insert':
$queryData = $event->getReply()->n;
break;
}
// And log query
$this->mongoEngine->logMongoQuery($queryString, $queryData, $queryTimings, []);
// And reset timings
$this->commandTimings = 0.0;
}
}

View File

@ -0,0 +1,234 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\DatabaseEngine;
use FuzeWorks\Exception\DatabaseException;
use MongoDB\ChangeStream;
use MongoDB\Client;
use MongoDB\Collection;
use MongoDB\Database;
use MongoDB\Driver\Exception\InvalidArgumentException;
use MongoDB\Driver\Manager;
use function MongoDB\Driver\Monitoring\addSubscriber;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Session;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\RuntimeException;
use MongoDB\Model\DatabaseInfoIterator;
/**
* Class MongoEngine
*
* The following additional methods can be accessed through the __call method
* @method array|object dropDatabase(string $databaseName, array $options = [])
* @method Manager getManager()
* @method ReadConcern getReadConcern()
* @method ReadPreference getReadPreference()
* @method array getTypeMap()
* @method WriteConcern getWriteConcern()
* @method DatabaseInfoIterator listDatabases(array $options = [])
* @method Collection selectCollection(string $databaseName, string $collectionName, array $options = [])
* @method Database selectDatabase(string $databaseName, array $options = [])
* @method Session startSession(array $options = [])
* @method ChangeStream watch(array $pipeline = [], array $options = [])
*/
class MongoEngine extends DatabaseDriver
{
/**
* Whether the Engine has been set up
*
* @var bool
*/
protected bool $setUp = false;
/**
* @var Client|null
*/
protected ?Client $mongoConnection = null;
/**
* Connection string with the database
*
* @var string|null
*/
protected ?string $uri = null;
/**
* Returns the name of this engine
*
* @return string
*/
public function getName(): string
{
return 'mongo';
}
public function getConnectionDescription(): string
{
if (is_null($this->mongoConnection))
return 'none';
return $this->uri;
}
/**
* Whether the database connection has been set up yet
*
* @return bool
*/
public function isSetup(): bool
{
return $this->setUp;
}
/**
* Method called by \FuzeWorks\Database to setUp the database connection
*
* @param array $parameters
* @return bool
* @throws DatabaseException
*/
public function setUp(array $parameters): bool
{
// Prepare variables for connection
$this->uri = $parameters['uri'] ?? null;
$uriOptions = $parameters['uriOptions'] ?? [];
$driverOptions = $parameters['driverOptions'] ?? [];
// Don't attempt and connect without a URI
if (is_null($this->uri))
throw new DatabaseException("Could not setUp MongoEngine. No URI provided");
// Import username and password
if (isset($parameters['username']) && isset($parameters['password']))
{
$uriOptions['username'] = $parameters['username'];
$uriOptions['password'] = $parameters['password'];
}
// And set FuzeWorks app name
$uriOptions['appname'] = 'FuzeWorks';
try {
$this->mongoConnection = new Client($this->uri, $uriOptions, $driverOptions);
} catch (InvalidArgumentException | RuntimeException $e) {
throw new DatabaseException("Could not setUp MongoEngine. MongoDB threw exception: '" . $e->getMessage() . "'");
}
// Set this engine as set up
$this->setUp = true;
// Register subscriber
$subscriber = new MongoCommandSubscriber($this);
addSubscriber($subscriber);
// And return true upon success
return true;
}
public function logMongoQuery(string $queryString, int $queryData, float $queryTimings, array $errorInfo = [])
{
$this->logQuery($queryString, $queryData, $queryTimings, $errorInfo);
}
/**
* Method called by \FuzeWorks\Database to tear down the database connection upon shutdown
*
* @return bool
*/
public function tearDown(): bool
{
// MongoDB does not require any action. Always return true
return true;
}
/**
* Call methods on the Mongo Connection
*
* @param $name
* @param $arguments
* @return mixed
*/
public function __call($name, $arguments)
{
return $this->mongoConnection->{$name}(...$arguments);
}
/**
* Get properties from the Mongo Connection
*
* @param $name
* @return Database
*/
public function __get($name): Database
{
return $this->mongoConnection->$name;
}
/**
* @return bool
*/
public function transactionStart(): bool
{
return false;
}
/**
* @return bool
*/
public function transactionEnd(): bool
{
return false;
}
/**
* @return bool
*/
public function transactionCommit(): bool
{
return false;
}
/**
* @return bool
*/
public function transactionRollback(): bool
{
return false;
}
}

View File

@ -0,0 +1,370 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\DatabaseEngine;
use FuzeWorks\Exception\DatabaseException;
use FuzeWorks\Exception\TransactionException;
use FuzeWorks\Logger;
use PDO;
use PDOException;
use PDOStatement;
/**
* Class PDOEngine Class
*
* The following additional methods can be accessed through the __call method
* @method bool exec(string $statement)
* @method mixed getAttribute(int $attribute)
* @method string lastInsertId(string $name = null)
* @method string quote(string $string, int $parameter_type = PDO::PARAM_STR)
* @method bool setAttribute(int $attribute, mixed $value)
*/
class PDOEngine extends DatabaseDriver
{
/**
* Whether the Engine has been set up
*
* @var bool
*/
protected bool $setUp = false;
/**
* The PDO object connected with the database
*
* @var PDO
*/
protected PDO $pdoConnection;
/**
* Connection string with the database
*
* @var string|null
*/
protected ?string $dsn = null;
/**
* Whether a transaction has failed and should be reverted in the future
*
* @var bool
*/
protected bool $transactionFailed = false;
/**
* Whether a transaction should be automatically committed if not manually aborted by the user.
*
* If enabled, will automatically commit or revert upon shutdown
* If disabled, will not do anything
*
* @var bool
*/
protected bool $transactionAutocommit = false;
/**
* Returns the name of this engine
*
* @return string
*/
public function getName(): string
{
return 'pdo';
}
public function getConnectionDescription(): string
{
return is_null($this->dsn) ? 'none' : $this->dsn;
}
/**
* Whether the database connection has been set up yet
*
* @return bool
*/
public function isSetup(): bool
{
return $this->setUp;
}
/**
* Method called by \FuzeWorks\Database to setUp the database connection
*
* @param array $parameters
* @return bool
* @throws DatabaseException
*/
public function setUp(array $parameters): bool
{
// Prepare variables for connection
$this->dsn = $parameters['dsn'] ?? null;
$username = $parameters['username'] ?? '';
$password = $parameters['password'] ?? '';
// Don't attempt connection without DSN
if (is_null($this->dsn))
throw new DatabaseException("Could not setUp PDOEngine. No DSN provided");
// Set some base parameters which are required for FuzeWorks
$parameters[PDO::ATTR_ERRMODE] = PDO::ERRMODE_SILENT;
// Attempt to connect. Throw exception on failure
try {
$this->pdoConnection = new PDO($this->dsn, $username, $password, $parameters);
} catch (PDOException $e) {
throw new DatabaseException("Could not setUp PDOEngine. PDO threw PDOException: '" . $e->getMessage() . "'");
}
// Set this engine as set up
$this->setUp = true;
// And return true upon success
return true;
}
/**
* Method called by \FuzeWorks\Database to tear down the database connection upon shutdown
*
* @return bool
* @throws TransactionException
*/
public function tearDown(): bool
{
// Commit or rollback all changes to the database
$this->transactionEnd();
return true;
}
/**
* Call methods on the PDO Connection
*
* @param $name
* @param $arguments
* @return mixed
*/
public function __call($name, $arguments)
{
return $this->pdoConnection->{$name}(...$arguments);
}
/**
* Get properties from the PDO Connection
*
* @param $name
* @return mixed
*/
public function __get($name)
{
return $this->pdoConnection->$name;
}
/**
* Set properties on the PDO Connection
*
* @param $name
* @param $value
* @return mixed
*/
public function __set($name, $value)
{
return $this->pdoConnection->$name = $value;
}
/**
* Internal method used to report queries by the \FuzeWorks\DatabaseEngine\PDOStatement
*
* @internal
* @param string $queryString
* @param int $queryData
* @param float $queryTimings
* @param array $errorInfo
*/
public function logPDOQuery(string $queryString, int $queryData, float $queryTimings, array $errorInfo = [])
{
$errorInfo = empty($errorInfo) ? $this->error() : $errorInfo;
$this->logQuery($queryString, $queryData, $queryTimings, $errorInfo);
}
/**
* Perform a query with the database. Only supports queries.
*
* Should only be used for reading data without dynamic statements.
*
* @param string $sql
* @return PDOStatement
* @throws DatabaseException
*/
public function query(string $sql): PDOStatement
{
if (empty($sql))
throw new DatabaseException("Could not run query. Provided query is empty.");
// Run the query and benchmark the time
$benchmarkStart = microtime(true);
$result = $this->pdoConnection->query($sql);
$benchmarkEnd = microtime(true) - $benchmarkStart;
// Log the query
$this->logPDOQuery($sql, 0, $benchmarkEnd);
// If the query failed, handle the error
if ($result === false)
{
// Mark the transaction as failed
$this->transactionFailed = true;
// And throw an exception
throw new DatabaseException("Could not run query. Database returned an error. Error code: " . $this->error()['code']);
}
return $result;
}
/**
* Create a PDOStatement to alter data on the database.
*
* @param string $statement
* @param array $driver_options
* @return PDOStatementWrapper
*/
public function prepare(string $statement, array $driver_options = []): PDOStatementWrapper
{
return new PDOStatementWrapper(
$this->pdoConnection->prepare($statement, $driver_options),
array($this, 'logPDOQuery'),
$this
);
}
/**
* Generates an error message for the last failure in PDO
*
* @return array
*/
protected function error(): array
{
$error = [];
$pdoError = $this->pdoConnection->errorInfo();
if (empty($pdoError[0]) || $pdoError[0] == '00000')
return $error;
$error['code'] = isset($pdoError[1]) ? $pdoError[0] . '/' . $pdoError[1] : $pdoError[0];
if (isset($pdoError[2]))
$error['message'] = $pdoError[2];
return $error;
}
/**
* Start a transaction
*
* @return bool
* @throws TransactionException
*/
public function transactionStart(): bool
{
try {
return $this->pdoConnection->beginTransaction();
} catch (PDOException $e) {
throw new TransactionException("Could not start transaction. PDO threw PDOException: '" . $e->getMessage() . "'");
}
}
/**
* End a transaction.
*
* Only runs of autocommit is enabled; and a transaction is running.
* Automatically rolls back changes if an error occurs with a query
*
* @return bool
* @throws TransactionException
*/
public function transactionEnd(): bool
{
// If autocommit is disabled, don't do anything
if (!$this->transactionAutocommit)
return false;
// If there is no transaction, there is nothing to rollback
if (!$this->pdoConnection->inTransaction())
return false;
// If a transaction has failed, it should be rolled back
if ($this->transactionFailed === true)
{
$this->transactionRollback();
Logger::logError("PDOEngine transaction failed. Transaction has been rolled back.");
}
return $this->transactionCommit();
}
/**
* Commit a transaction
*
* @return bool
* @throws TransactionException
*/
public function transactionCommit(): bool
{
try {
return $this->pdoConnection->commit();
} catch (PDOException $e) {
throw new TransactionException("Could not commit transaction. PDO threw PDOException: '" . $e->getMessage() . "'");
}
}
/**
* Roll back a transaction
*
* @return bool
* @throws TransactionException
*/
public function transactionRollback(): bool
{
try {
return $this->pdoConnection->rollBack();
} catch (PDOException $e) {
throw new TransactionException("Could not rollback transaction. PDO threw PDOException: '" . $e->getMessage() . "'");
}
}
/**
* @internal
*/
public function transactionFail()
{
$this->transactionFailed = true;
}
}

View File

@ -0,0 +1,172 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\DatabaseEngine;
use FuzeWorks\Exception\DatabaseException;
use PDOStatement;
use PDO;
/**
* Class PDOStatementWrapper
*
* Provides a wrapper for PDOStatement objects so that these can be logged
*
* The following additional methods can be accessed through the __call method:
* @method bool bindColumn(mixed $column, mixed &$param, int $type = null, int $maxlen = null, mixed $driverdata = null)
* @method bool bindParam(mixed $parameter, mixed &$variable, int $data_type = PDO::PARAM_STR, int $length = null, mixed $driver_options = null)
* @method bool bindValue(mixed $parameter, mixed $value, int $data_type = PDO::PARAM_STR)
* @method bool closeCursor()
* @method int columnCount()
* @method void debugDumpParams()
* @method string errorCode()
* @method array errorInfo()
* @method mixed fetch(int $fetch_style = null, int $cursor_orientation = PDO::FETCH_ORI_NEXT, int $cursor_offset = 0)
* @method array fetchAll(int $fetch_style = null, mixed $fetch_argument = null, array $ctor_args = array())
* @method mixed fetchColumn(int $column_number = 0)
* @method mixed fetchObject(string $class_name = "stdClass", array $ctor_args = array())
* @method mixed getAttribute(int $attribute)
* @method array getColumnMeta(int $column)
* @method bool nextRowset()
* @method int rowCount()
* @method bool setAttribute(int $attribute, mixed $value)
* @method bool setFetchMode(int $mode)
*/
class PDOStatementWrapper
{
/**
* @var PDOStatement
*/
private PDOStatement $statement;
/**
* Callable for logging queries and errors
*
* @var callable
*/
private $logQueryCallable;
/**
* @var PDOEngine
*/
private PDOEngine $engine;
public function __construct(PDOStatement $statement, callable $logQueryCallable, PDOEngine $engine)
{
$this->statement = $statement;
$this->logQueryCallable = $logQueryCallable;
$this->engine = $engine;
}
/**
* @param array $input_parameters
* @return bool
* @throws DatabaseException
*/
public function execute(array $input_parameters = []): bool
{
// Run the query and benchmark the time
$benchmarkStart = microtime(true);
if (empty($input_parameters))
$result = $this->statement->execute();
else
$result = $this->statement->execute($input_parameters);
$benchmarkEnd = microtime(true) - $benchmarkStart;
$errInfo = $this->error();
call_user_func_array($this->logQueryCallable, [$this->statement->queryString, $this->statement->rowCount(), $benchmarkEnd, $errInfo]);
// If the query failed, throw an error
if ($result === false)
{
$this->engine->transactionFail();
// And throw an exception
throw new DatabaseException("Could not run query. Database returned an error. Error code: " . $errInfo['code']);
}
return true;
}
/**
* Retrieves the statement last used
*
* @return PDOStatement
*/
public function getStatement(): PDOStatement
{
return $this->statement;
}
/**
* Returns the query string
*
* @return string
*/
public function getQuery(): string
{
return $this->statement->queryString;
}
/**
* Generates an error message for the last failure in PDO
*
* @return array
*/
private function error(): array
{
$error = [];
$pdoError = $this->statement->errorInfo();
if (empty($pdoError[0]) || $pdoError[0] == '00000')
return $error;
$error['code'] = isset($pdoError[1]) ? $pdoError[0] . '/' . $pdoError[1] : $pdoError[0];
if (isset($pdoError[2]))
$error['message'] = $pdoError[2];
return $error;
}
public function __call($method, $parameters)
{
return call_user_func_array([$this->statement, $method], $parameters);
}
public function __get($name)
{
return $this->statement->$name;
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\DatabaseEngine;
interface iDatabaseEngine
{
public function getName(): string;
public function getConnectionDescription(): string;
public function isSetup(): bool;
public function setUp(array $parameters): bool;
public function tearDown(): bool;
/**
* @return bool
*/
public function transactionStart(): bool;
/**
* @return bool
*/
public function transactionEnd(): bool;
/**
* @return bool
*/
public function transactionCommit(): bool;
/**
* @return bool
*/
public function transactionRollback(): bool;
}

View File

@ -0,0 +1,154 @@
<?php
/**
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.1.4
*
* @version Version 1.1.4
*/
namespace FuzeWorks;
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
use Tracy\IBarPanel;
use Tracy\Debugger;
/**
* DatabaseTracyBridge Class.
*
* This class provides a bridge between FuzeWorks\Database and Tracy Debugging tool.
*
* This class registers in Tracy, and creates a Bar object which contains information about database sessions.
* It hooks into database usage and provides the information on the Tracy Bar panel.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
*/
class DatabaseTracyBridge implements IBarPanel
{
/**
* @var iDatabaseEngine[]
*/
public static array $databases = array();
protected array $results = array();
public static function register()
{
$class = new self();
$bar = Debugger::getBar();
$bar->addPanel($class);
}
public static function registerDatabase(iDatabaseEngine $database)
{
self::$databases[] = $database;
}
protected function getResults(): array
{
if (!empty($this->results))
{
return $this->results;
}
// First prepare global variables
$results = array();
$results['dbCount'] = 0;
$results['queryCount'] = 0;
$results['queryTimings'] = 0.0;
$results['errorsFound'] = false;
// Go through all databases
foreach (self::$databases as $database) {
// Increase total databases
$results['dbCount']++;
// First determine the ID
$databaseId = $database->getConnectionDescription();
// Go through all queries
if (!method_exists($database, 'getQueries'))
{
continue;
}
foreach ($database->getQueries() as $query)
{
$results['queryTimings'] += $query['queryTimings'];
$key = $query['queryString'];
if (!isset($results['queries'][$databaseId][$key]))
$results['queryCount']++;
$results['queries'][$databaseId][$key]['query'] = $query['queryString'];
$results['queries'][$databaseId][$key]['timings'] = $query['queryTimings'];
$results['queries'][$databaseId][$key]['errors'] = $query['queryError'];
if (!isset($results['queries'][$databaseId][$key]['data']))
$results['queries'][$databaseId][$key]['data'] = $query['queryData'];
else
$results['queries'][$databaseId][$key]['data'] += $query['queryData'];
if (!empty($query['queryError']))
$results['errorsFound'] = true;
}
}
// Limit the amount in order to keep things readable
$results['queryCountProvided'] = 0;
if (isset($results['queries']))
{
foreach ($results['queries'] as $id => $database) {
$results['queries'][$id] = array_reverse(array_slice($database, -10));
$results['queryCountProvided'] += count($results['queries'][$id]);
}
$results = array_slice($results, -10);
}
return $this->results = $results;
}
public function getTab(): string
{
$results = $this->getResults();
ob_start(function () {});
require dirname(__DIR__) . DS . 'Layout' . DS . 'layout.tracydatabasetab.php';
return ob_get_clean();
}
public function getPanel(): string
{
// Parse the panel
$results = $this->getResults();
ob_start(function () {});
require dirname(__DIR__) . DS . 'Layout' . DS . 'layout.tracydatabasepanel.php';
return ob_get_clean();
}
}

View File

@ -0,0 +1,86 @@
<?php
/**
* FuzeWorks.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2015 TechFuze
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2017, TechFuze. (http://techfuze.net)
* @copyright Copyright (c) 1996 - 2015, Free Software Foundation, Inc. (http://www.fsf.org/)
* @license http://opensource.org/licenses/GPL-3.0 GPLv3 License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.0.4
*
* @version Version 1.0.4
*/
namespace FuzeWorks\Event;
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
use FuzeWorks\Event;
/**
* Event that gets loaded when a database driver is loaded
*
* Use this to cancel the loading of a database, or change the provided database
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2017, TechFuze. (http://techfuze.net)
*/
class DatabaseLoadDriverEvent extends Event
{
/**
* A possible database that can be loaded.
*
* Provide a database in this variable and it will be loaded. It shall be identified as default if
* the parameters variable is empty. If there is a string in parameters this database shall be identified as
* such.
*
* @var iDatabaseEngine|null
*/
public ?iDatabaseEngine $databaseEngine = null;
/**
* The name of the engine to be loaded
*
* @var string
*/
public string $engineName;
/**
* Parameters of the database to be loaded
*
* @var array
*/
public array $parameters;
/**
* Database group to load
*
* @var string
*/
public string $connectionName;
public function init(string $engineName, array $parameters, string $connectionName)
{
$this->engineName = $engineName;
$this->parameters = $parameters;
$this->connectionName = $connectionName;
}
}

View File

@ -1,10 +1,10 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -25,73 +25,66 @@
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Event;
use FuzeWorks\Event;
use FuzeWorks\Model\iDatabaseTableModel;
/**
* Event that gets loaded when a layout is loaded.
*
* Use this to cancel the loading of a layout, or change the file or engine of a layout
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class LayoutLoadEvent extends Event
class DatabaseLoadTableModelEvent extends Event
{
/**
* The directory of the layout to be loaded.
* A possible database that can be loaded.
*
* Provide a database in this variable and it will be loaded. It shall be identified as default if
* the parameters variable is empty. If there is a string in parameters this database shall be identified as
* such.
*
* @var iDatabaseTableModel|null
*/
public ?iDatabaseTableModel $tableModel = null;
/**
* The name of the engine to be loaded
*
* @var string
*/
public $directory;
public string $engineName;
/**
* The file of the layout to be loaded.
* The name of the table this model manages
*
* @var string
*/
public $file;
public string $tableName;
/**
* The engine the file will be loaded with.
*
* @var object
*/
public $engine;
/**
* The assigned variables to the template.
* Parameters of the database to be loaded
*
* @var array
*/
public $assigned_variables;
public function init($file, $directory, $engine, $assigned_variables)
{
$this->file = $file;
$this->directory = $directory;
$this->engine = $engine;
$this->assigned_variables = $assigned_variables;
}
public array $parameters;
/**
* Assign a variable for the template.
* Database group to load
*
* @param string $key Key of the variable
* @param mixed $value Value of the variable
* @var string
*/
public function assign($key, $value)
public string $connectionName;
public function init(string $engineName, array $parameters, string $connectionName, string $tableName)
{
$this->assigned_variables[$key] = $value;
$this->engineName = $engineName;
$this->parameters = $parameters;
$this->connectionName = $connectionName;
$this->tableName = $tableName;
}
}
}

View File

@ -1,72 +0,0 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Event;
use FuzeWorks\Event;
/**
* Event that gets loaded when a layout contents is about to be displayed.
*
* Use this to cancel the echo of the layout output. This can also be used to intercept the output.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class LayoutDisplayEvent extends Event
{
/**
* @var string Contents of the layout
*/
public $contents;
/**
* @var string File. File that the contents derived from
*/
public $file;
/**
* @var array directories. Directories that the layout file might resides in.
*/
public $directories;
public function init(string $contents, string $file, array $directories)
{
$this->contents = $contents;
$this->file = $file;
$this->directories = $directories;
}
}

View File

@ -1,6 +1,6 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
@ -29,21 +29,19 @@
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
* @since Version 1.1.4
*
* @version Version 1.2.0
* @version Version 1.1.4
*/
namespace FuzeWorks\Exception;
/**
* Class LayoutException.
* Class DatabaseException.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class LayoutException extends Exception
class DatabaseException extends Exception
{
}
?>
}

View File

@ -1,10 +1,10 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -25,11 +25,11 @@
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
@ -37,13 +37,11 @@
namespace FuzeWorks\Exception;
/**
* Class LanguageException.
* Class DatabaseException.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
*/
class LanguageException extends Exception
class TransactionException extends Exception
{
}
?>
}

View File

@ -1,533 +0,0 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
*
* @version Version 1.2.0
*/
namespace FuzeWorks;
use FuzeWorks\Event\LayoutLoadEvent;
use FuzeWorks\TemplateEngine\{JsonEngine,PHPEngine,SmartyEngine,LatteEngine,TemplateEngine};
use FuzeWorks\Exception\LayoutException;
use FuzeWorks\Exception\EventException;
/**
* Layout and Template Manager for FuzeWorks.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class Layout
{
use ComponentPathsTrait;
/**
* @var Factory
*/
protected $factory;
/**
* The file which the current template is loaded from
*
* @var null|string
*/
public $file = null;
/**
* The directory where the current template is loaded from
*
* @var null|string
*/
public $directory = null;
/**
* All assigned currently assigned to the template.
*
* @var array Associative Assigned Variable Array
*/
protected $assigned_variables = array();
/**
* All engines that can be used for templates.
*
* @var array of engines
*/
protected $engines = array();
/**
* All file extensions that can be used and are bound to a template engine.
*
* @var array of names of engines
*/
protected $file_extensions = array();
/**
* whether the template engines are already called.
*
* @var bool True if loaded
*/
protected $engines_loaded = false;
/**
* The currently selected template engine.
*
* @var TemplateEngine
*/
protected $current_engine;
/**
* Standard Component method for initializing components after adding extensions
*/
public function init()
{
$this->factory = Factory::getInstance();
}
/**
* Retrieve a template file using a string and a directory and immediatly parse it to the output class.
*
* What template file gets loaded depends on the template engine that is being used.
* PHP for example uses .php files. Providing this function with 'home/dashboard' will load the home/layout.dashboard.php file.
* You can also provide no particular engine, and the manager will decide what template to load.
* Remember that doing so will result in a LayoutException when multiple compatible files are found.
*
* @param string $file File to load
* @param array $directories Directories to load it from, uses componentPaths if none provided
*
* @return mixed
* @throws LayoutException On error
* @throws EventException
* @throws Exception\ConfigException
*/
public function display(string $file, array $directories = []): bool
{
$contents = $this->get($file, $directories);
$event = Events::fireEvent('layoutDisplayEvent', $contents, $file, $directories);
if (!$event->isCancelled())
echo $event->contents;
return true;
}
/**
* Retrieve a template file using a string and a directory.
*
* What template file gets loaded depends on the template engine that is being used.
* PHP for example uses .php files. Providing this function with 'home/dashboard' will load the home/layout.dashboard.php file.
* You can also provide no particular engine, and the manager will decide what template to load.
* Remember that doing so will result in a LayoutException when multiple compatible files are found.
*
* @param string $file File to load
* @param array $directories Directory to load it from
*
* @return string The output of the template
* @throws LayoutException On error
*/
public function get(string $file, array $directories = []): string
{
Logger::newLevel("Loading template file '".$file."'");
// Determine what directories should be checked
$directories = (empty($directories) ? $this->componentPaths : [3 => $directories]);
// First load the template engines
$this->loadTemplateEngines();
// First retrieve the filePath
if (is_null($this->current_engine)) {
$this->setFileFromString($file, $directories, array_keys($this->file_extensions));
} else {
$this->setFileFromString($file, $directories, $this->current_engine->getFileExtensions());
}
// Then assign some basic variables for the template
// @TODO: Implement csrfTokenName and csrfHash from security under layoutLoadEvent
// Select an engine if one is not already selected
if (is_null($this->current_engine)) {
$this->current_engine = $this->getEngineFromExtension($this->getExtensionFromFile($this->file));
}
$this->current_engine->setDirectory($this->directory);
// And run an Event to see what other parts have to say about it
try {
/** @var LayoutLoadEvent $event */
$event = Events::fireEvent('layoutLoadEvent', $this->file, $this->directory, $this->current_engine, $this->assigned_variables);
// @codeCoverageIgnoreStart
} catch (EventException $e) {
throw new LayoutException("layoutEvent threw exception: '".$e->getMessage()."''", 1);
// @codeCoverageIgnoreEnd
}
// The event has been cancelled
if ($event->isCancelled())
return 'cancelled';
// And re-fetch the data from the event
$this->current_engine = $event->engine;
$this->assigned_variables = $event->assigned_variables;
Logger::stopLevel();
// And finally run it
if (file_exists($event->file)) {
return $this->current_engine->get($event->file, $this->assigned_variables);
}
throw new LayoutException('The requested file was not found', 1);
}
/**
* Retrieve a Template Engine from a File Extension.
*
* @param string $extension File extension to look for
*
* @return TemplateEngine
* @throws LayoutException
*/
public function getEngineFromExtension($extension): TemplateEngine
{
if (isset($this->file_extensions[strtolower($extension)]))
return $this->engines[ $this->file_extensions[strtolower($extension)]];
throw new LayoutException('Could not get Template Engine. No engine has corresponding file extension', 1);
}
/**
* Retrieve the extension from a file string.
*
* @param string $fileString The path to the file
*
* @return string Extension of the file
*/
public function getExtensionFromFile($fileString): string
{
return substr($fileString, strrpos($fileString, '.') + 1);
}
/**
* Converts a layout string to a file using the directory and the used extensions.
*
* It will detect whether the file exists and choose a file according to the provided extensions
*
* @param string $string The string used by a controller. eg: 'dashboard/home'
* @param array $directories The directories to search in for the template
* @param array $extensions Extensions to use for this template. Eg array('php', 'tpl') etc.
*
* @return array File and directory
* @throws LayoutException On error
*/
public function getFileFromString(string $string, array $directories, array $extensions = []): array
{
// @TODO Malformed strings pass. Write better function
if (strpbrk($string, "\\/?%*:|\"<>") === TRUE)
{
// @codeCoverageIgnoreStart
throw new LayoutException('Could not get file. Invalid file string', 1);
// @codeCoverageIgnoreEnd
}
// Set the file name and location
$layoutSelector = explode('/', $string);
if (count($layoutSelector) == 1) {
$layoutSelector = 'layout.'.$layoutSelector[0];
} else {
// Get last file
$file = end($layoutSelector);
// Reset to start
reset($layoutSelector);
// Remove last value
array_pop($layoutSelector);
$layoutSelector[] = 'layout.'.$file;
// And create the final value
$layoutSelector = implode(DS, $layoutSelector);
}
// Iterate over componentPaths
for ($i=Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++)
{
if (!isset($directories[$i]))
continue;
foreach ($directories[$i] as $directory)
{
// Then try and select a file
$fileSelected = false;
$selectedFile = null;
foreach ($extensions as $extension) {
$file = $directory.DS.$layoutSelector.'.'.strtolower($extension);
$file = preg_replace('#/+#', '/', $file);
if (file_exists($file) && !$fileSelected) {
$selectedFile = $file;
$fileSelected = true;
Logger::log("Found matching file: '".$file."'");
} elseif (file_exists($file) && $fileSelected) {
throw new LayoutException('Could not select template. Multiple valid extensions detected. Can not choose.', 1);
}
}
if ($fileSelected)
return ['file' => $selectedFile, 'directory' => $directory];
}
}
throw new LayoutException('Could not select template. No matching file found.');
}
/**
* Converts a layout string to a file using the directory and the used extensions.
* It also sets the file variable of this class.
*
* It will detect whether the file exists and choose a file according to the provided extensions
*
* @param string $string The string used by a controller. eg: 'dashboard/home'
* @param array $directories The directory to search in for the template
* @param array $extensions Extensions to use for this template. Eg array('php', 'tpl') etc.
*
* @throws LayoutException On error
*/
public function setFileFromString($string, array $directories, $extensions = array())
{
$arr = $this->getFileFromString($string, $directories, $extensions);
$this->file = $arr['file'];
$this->directory = $arr['directory'];
}
/**
* Get the current file to be loaded.
*
* @return null|string Path to the file
*/
public function getFile()
{
return $this->file;
}
/**
* Set the file to be loaded.
*
* @param string $file Path to the file
*/
public function setFile($file)
{
$this->file = $file;
}
/**
* Get the directory of the file to be loaded.
*
* @return null|string Path to the directory
*/
public function getDirectory()
{
return $this->directory;
}
/**
* Set the directory of the file to be loaded.
*
* @param string $directory Path to the directory
*/
public function setDirectory($directory)
{
$this->directory = $directory;
}
/**
* Assign a variable for the template.
*
* @param string $key Key of the variable
* @param mixed $value Value of the variable
*/
public function assign($key, $value)
{
$this->assigned_variables[$key] = $value;
}
/**
* Set the title of the template.
*
* @param string $title title of the template
*/
public function setTitle($title)
{
$this->assigned_variables['title'] = $title;
}
/**
* Get the title of the template.
*
* @return string|bool title of the template
*/
public function getTitle()
{
if (!isset($this->assigned_variables['title']))
return false;
return $this->assigned_variables['title'];
}
/**
* Set the engine for the next layout.
*
* @param string $name Name of the template engine
*
* @return bool true on success
* @throws LayoutException on error
*/
public function setEngine($name): bool
{
$this->loadTemplateEngines();
if (isset($this->engines[$name])) {
$this->current_engine = $this->engines[$name];
Logger::log('Set the Template Engine to '.$name);
return true;
}
throw new LayoutException('Could not set engine. Engine does not exist', 1);
}
/**
* Get a loaded template engine.
*
* @param string $name Name of the template engine
*
* @return TemplateEngine
* @throws LayoutException
*/
public function getEngine(string $name): TemplateEngine
{
$this->loadTemplateEngines();
if (isset($this->engines[$name])) {
return $this->engines[$name];
}
throw new LayoutException('Could not return engine. Engine does not exist', 1);
}
/**
* Register a new template engine.
*
* @param TemplateEngine $engineClass Object that implements the \FuzeWorks\TemplateEngine
* @param string $engineName Name of the template engine
* @param array $engineFileExtensions File extensions this template engine should be used for
*
* @return bool true on success
* @throws LayoutException
*/
public function registerEngine(TemplateEngine $engineClass, string $engineName, array $engineFileExtensions = []): bool
{
// First check if the engine already exists
if (isset($this->engines[$engineName]))
throw new LayoutException("Could not register engine. Engine '".$engineName."' already registered", 1);
// Install it
$this->engines[$engineName] = $engineClass;
// Then install them
foreach ($engineFileExtensions as $extension) {
if (isset($this->file_extensions[strtolower($extension)])) {
throw new LayoutException('Could not register engine. File extension already bound to engine', 1);
}
// And add it
$this->file_extensions[strtolower($extension)] = $engineName;
}
// And log it
Logger::log('Registered Template Engine: '.$engineName);
return true;
}
/**
* Load the template engines by sending a layoutLoadEngineEvent.
* @throws LayoutException
* @returns bool True on loading. False when already loaded
*/
public function loadTemplateEngines(): bool
{
if (!$this->engines_loaded) {
// Fire Engine Event
try {
Events::fireEvent('layoutLoadEngineEvent');
} catch (Exception\EventException $e) {
throw new LayoutException("Could not loadTemplateEngines. layoutLoadEngineEvent threw exception: '".$e->getMessage()."''", 1);
}
// Load the engines provided in this file
// PHP Engine
$this->registerEngine(new PHPEngine(), 'PHP', array('php'));
// JSON Engine
if (extension_loaded('json'))
$this->registerEngine(new JsonEngine(), 'JSON', array('json'));
// Latte Engine
if (class_exists('\Latte\Engine', true))
$this->registerEngine(new LatteEngine(), 'Latte', array('latte'));
// Smarty Engine
if (class_exists('\Smarty', true))
$this->registerEngine(new SmartyEngine(), 'Smarty', array('tpl'));
$this->engines_loaded = true;
return true;
}
return false;
}
/**
* Resets the layout manager to its default state.
*/
public function reset()
{
if (!is_null($this->current_engine))
$this->current_engine->reset();
// Unload the engines
$this->engines = array();
$this->engines_loaded = false;
$this->file_extensions = array();
$this->current_engine = null;
$this->assigned_variables = array();
Logger::log('Reset the layout manager to its default state');
}
}

View File

@ -0,0 +1,284 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Model;
use FuzeWorks\Database;
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
use FuzeWorks\DatabaseEngine\MongoEngine;
use FuzeWorks\Exception\DatabaseException;
use MongoDB\Collection;
class MongoTableModel implements iDatabaseTableModel
{
/**
* Holds the FuzeWorks Database loader
*
* @var Database
*/
private Database $databases;
/**
* Whether the tableModel has been properly setup
*
* @var bool
*/
protected bool $setup = false;
/**
* Holds the PDOEngine for this model
*
* @var MongoEngine
*/
protected MongoEngine $dbEngine;
/**
* Holds the collection that is being modified
*
* @var Collection
*/
protected Collection $collection;
/**
* Initializes the model
*
* @param iDatabaseEngine $engine
* @param string $tableName
* @throws DatabaseException
*/
public function setUp(iDatabaseEngine $engine, string $tableName): void
{
if (!$engine instanceof MongoEngine)
throw new DatabaseException('MongoTableModel can only be used with a MongoEngine');
$this->dbEngine = $engine;
$this->collection = $this->getCollection($tableName);
$this->setup = true;
}
public function isSetup(): bool
{
return $this->setup;
}
/**
* @param string $collectionString
* @return Collection
* @throws DatabaseException
*/
protected function getCollection(string $collectionString): Collection
{
// Determine collection
$coll = explode('.', $collectionString);
if (count($coll) != 2)
throw new DatabaseException("Could not load MongoTableModel. Provided tableName is not a valid collection string.");
return $this->dbEngine->{$coll[0]}->{$coll[1]};
}
/**
* @return string
*/
public function getName(): string
{
return 'mongo';
}
/**
* @return string
*/
public function getEngineName(): string
{
return 'mongo';
}
/**
* @return iDatabaseEngine
* @throws DatabaseException
*/
public function getEngine(): iDatabaseEngine
{
if (!$this->setup)
throw new DatabaseException("Could not return Engine. Engine not setup yet.");
return $this->dbEngine;
}
/**
* @param array $data
* @param array $options
* @param string $table
* @return int
* @throws DatabaseException
*/
public function create(array $data, array $options = [], string $table = 'default'): int
{
// If not data is provided, stop now
if (empty($data))
throw new DatabaseException("Could not create data. No data provided.");
// Select collection
if ($table == 'default')
$collection = $this->collection;
else
$collection = $this->getCollection($table);
// And execute the request
if ($this->arrIsAssoc($data))
$res = $collection->insertOne($data, $options);
else
$res = $collection->insertMany($data, $options);
// And return the count of inserted documents
return $res->getInsertedCount();
}
/**
* @param array $filter
* @param array $options
* @param string $table
* @return TableModelResult
* @throws DatabaseException
*/
public function read(array $filter = [], array $options = [], string $table = 'default'): TableModelResult
{
// Select collection
if ($table == 'default')
$collection = $this->collection;
else
$collection = $this->getCollection($table);
// Execute the request
$results = $collection->find($filter, $options);
// Convert the result into a TableModelResult
return new TableModelResult($results);
}
/**
* @param array $data
* @param array $filter
* @param array $options
* @param string $table
* @return int
* @throws DatabaseException
*/
public function update(array $data, array $filter, array $options = [], string $table = 'default'): int
{
// If not data is provided, stop now
if (empty($data))
throw new DatabaseException("Could not create data. No data provided.");
// Select collection
if ($table == 'default')
$collection = $this->collection;
else
$collection = $this->getCollection($table);
// And execute the request
$data = ['$set' => $data];
$res = $collection->updateMany($filter, $data, $options);
// Return the result
return $res->getModifiedCount();
}
/**
* @param array $filter
* @param array $options
* @param string $table
* @return int
* @throws DatabaseException
*/
public function delete(array $filter, array $options = [], string $table = 'default'): int
{
// Select collection
if ($table == 'default')
$collection = $this->collection;
else
$collection = $this->getCollection($table);
// Execute the request
$res = $collection->deleteMany($filter, $options);
// Return the result
return $res->getDeletedCount();
}
/**
* @return bool
*/
public function transactionStart(): bool
{
return $this->dbEngine->transactionStart();
}
/**
* @return bool
*/
public function transactionEnd(): bool
{
return $this->dbEngine->transactionEnd();
}
/**
* @return bool
*/
public function transactionCommit(): bool
{
return $this->dbEngine->transactionCommit();
}
/**
* @return bool
*/
public function transactionRollback(): bool
{
return $this->dbEngine->transactionRollback();
}
/**
* Determines whether an array is associative or numeric
*
* @param array $arr
* @return bool
*/
private function arrIsAssoc(array $arr): bool
{
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
}

View File

@ -0,0 +1,369 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Model;
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
use FuzeWorks\DatabaseEngine\PDOEngine;
use FuzeWorks\DatabaseEngine\PDOStatementWrapper;
use FuzeWorks\Exception\DatabaseException;
use FuzeWorks\Exception\TransactionException;
use PDO;
use PDOStatement;
/**
* PDOTableModel Class
*
* A PDO Wrapper allowing the user to modify a table without using CRUD. No writing of SQL required!
*
* The following additional methods can be accessed through the __call method
* @method PDOStatement query(string $sql)
* @method PDOStatementWrapper prepare(string $statement, array $driver_options = [])
* @method bool exec(string $statement)
* @method mixed getAttribute(int $attribute)
* @method string lastInsertId(string $name = null)
* @method string quote(string $string, int $parameter_type = PDO::PARAM_STR)
* @method bool setAttribute(int $attribute, mixed $value)
*/
class PDOTableModel implements iDatabaseTableModel
{
/**
* Holds the PDOEngine for this model
*
* @var PDOEngine
*/
protected PDOEngine $dbEngine;
/**
* Whether the tableModel has been properly setup
*
* @var bool
*/
protected bool $setup = false;
/**
* The table this model manages on the database
*
* @var string
*/
protected string $tableName;
/**
* The last statement used by PDO
*
* @var PDOStatementWrapper
*/
protected PDOStatementWrapper $lastStatement;
/**
* Initializes the model
*
* @param iDatabaseEngine $engine
* @param string $tableName
* @throws DatabaseException
*/
public function setUp(iDatabaseEngine $engine, string $tableName): void
{
if (!$engine instanceof PDOEngine)
throw new DatabaseException('PDOTableModel can only be used with PDOEngine');
$this->dbEngine = $engine;
$this->tableName = $tableName;
$this->setup = true;
}
public function isSetup(): bool
{
return $this->setup;
}
public function getName(): string
{
return 'pdo';
}
/**
* @return string
*/
public function getEngineName(): string
{
return 'pdo';
}
/**
* @return iDatabaseEngine
* @throws DatabaseException
*/
public function getEngine(): iDatabaseEngine
{
if (!$this->setup)
throw new DatabaseException("Could not return Engine. Engine not setup yet.");
return $this->dbEngine;
}
/**
* @param array $data
* @param array $options
* @return int
* @throws DatabaseException
*/
public function create(array $data, array $options = []): int
{
// If no data is provided, stop now
if (empty($data))
throw new DatabaseException("Could not create data. No data provided.");
// Determine which fields will be inserted
$fieldsArr = $this->createFields($data);
$fields = $fieldsArr['fields'];
$values = $fieldsArr['values'];
// Generate the sql and create a PDOStatement
$sql = "INSERT INTO {$this->tableName} ({$fields}) VALUES ({$values})";
/** @var PDOStatement $statement */
$this->lastStatement = $this->dbEngine->prepare($sql);
// And execute the query
if ($this->arrIsAssoc($data))
$this->lastStatement->execute($data);
else
foreach ($data as $record)
$this->lastStatement->execute($record);
// And return true for success
return $this->lastStatement->rowCount();
}
/**
* @param array $filter
* @param array $options
* @return TableModelResult
* @throws DatabaseException
*/
public function read(array $filter = [], array $options = []): TableModelResult
{
// Determine which fields to select. If none provided, select all
$fields = (isset($options['fields']) && is_array($options['fields']) ? implode(',', $options['fields']) : '*');
$limit = isset($options['limit']) ? 'LIMIT ' . intval($options['limit']) : '';
// Apply the filter. If none provided, don't condition it
$where = $this->filter($filter);
// Generate the sql and create a PDOStatement
$sql = "SELECT " . $fields . " FROM {$this->tableName} " . $where . " " . $limit;
/** @var PDOStatement $statement */
$this->lastStatement = $this->dbEngine->prepare($sql);
// And execute the query
foreach ($filter as $key => $val)
{
if (is_null($val))
$this->lastStatement->bindValue(':' . $key, $val, PDO::PARAM_NULL);
else
$this->lastStatement->bindValue(':' . $key, $val);
}
$this->lastStatement->execute();
// Fetch PDO Iterable
return new TableModelResult($this->lastStatement->getStatement());
}
public function update(array $data, array $filter, array $options = []): int
{
// If no data is provided, stop now
if (empty($data))
throw new DatabaseException("Could not update data. No data provided.");
// Apply the filter
$where = $this->filter($filter);
// Determine fields and values
$fields = [];
foreach ($data as $key => $val)
$fields[] = $key."=:".$key;
$fields = implode(', ', $fields);
// Generate the sql and create a PDOStatement
$sql = "UPDATE {$this->tableName} SET {$fields} {$where}";
/** @var PDOStatement $statement */
$this->lastStatement = $this->dbEngine->prepare($sql);
// Merge data and filter, since both are needed by the statement
$parameters = array_merge($data, $filter);
// And execute the query
$this->lastStatement->execute($parameters);
// And return true for success
return $this->lastStatement->rowCount();
}
public function delete(array $filter, array $options = []): int
{
// Apply the filter
$where = $this->filter($filter);
$limit = isset($options['limit']) ? 'LIMIT ' . intval($options['limit']) : '';
// Generate the sql and create a PDOStatement
$sql = "DELETE FROM {$this->tableName} " . $where . " " . $limit;
/** @var PDOStatement $statement */
$this->lastStatement = $this->dbEngine->prepare($sql);
// And execute the query
$this->lastStatement->execute($filter);
// And return true for success
return $this->lastStatement->rowCount();
}
/**
* @return bool
* @throws TransactionException
*/
public function transactionStart(): bool
{
return $this->dbEngine->transactionStart();
}
/**
* @return bool
* @throws TransactionException
*/
public function transactionEnd(): bool
{
return $this->dbEngine->transactionEnd();
}
/**
* @return bool
* @throws TransactionException
*/
public function transactionCommit(): bool
{
return $this->dbEngine->transactionCommit();
}
/**
* @return bool
* @throws TransactionException
*/
public function transactionRollback(): bool
{
return $this->dbEngine->transactionRollback();
}
/**
* Call methods on the PDO Engine, which calls methods on the PDO Connection
*
* @param $name
* @param $arguments
* @return PDOEngine
*/
public function __call($name, $arguments)
{
return $this->dbEngine->{$name}(...$arguments);
}
/**
* Get properties from the PDO Engine, which gets properties from the PDO connection
*
* @param $name
* @return mixed
*/
public function __get($name)
{
return $this->dbEngine->$name;
}
/**
* Set properties on the PDO Connection, which sets properties on the PDO Connection
*
* @param $name
* @param $value
* @return mixed
*/
public function __set($name, $value)
{
return $this->dbEngine->$name = $value;
}
private function filter(array $filter = []): string
{
if (empty($filter))
return '';
$whereKeys = [];
foreach ($filter as $filterKey => $filterVal)
$whereKeys[] = $filterKey . '=:' . $filterKey;
return 'WHERE ' . implode(' AND ', $whereKeys);
}
private function createFields(array $record): array
{
// If multiple data is inserted at once, search the fields in the first entry
if (!$this->arrIsAssoc($record))
$record = $record[0];
// Determine the fields and values
$fields = [];
$values = [];
foreach ($record as $key => $val)
{
$fields[] = $key;
$values[] = ':'.$key;
}
// And merge them again
$fields = implode(', ', $fields);
$values = implode(', ', $values);
return ['fields' => $fields, 'values' => $values];
}
private function arrIsAssoc(array $arr): bool
{
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
}

View File

@ -0,0 +1,164 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Model;
use ArrayObject;
use IteratorAggregate;
use Traversable;
class TableModelResult implements IteratorAggregate
{
/**
* Raw result from the TableModel
*
* @var Traversable
*/
private iterable $raw;
/**
* Result from the TableModel, possibly altered by TableModelResult methods.
*
* @var ArrayObject
*/
private ArrayObject $result;
/**
* @var Traversable
*/
private iterable $traversable;
/**
* Whether the raw input has already been fully fetched
*
* @var bool
*/
private bool $fullyFetched = false;
public function __construct(iterable $results)
{
$this->raw = $results;
$this->traversable = $results;
}
/**
* Group the results by a certain field.
*
* @param string $field
* @return TableModelResult
*/
public function group(string $field): self
{
// First make sure all data is fetched
$this->allToArray();
// Afterwards build a grouped array
$grouped = [];
foreach ($this->result->getIterator() as $key => $val)
{
// Check if this group exists within the results
if (isset($val[$field]))
{
// Name of the group
$fieldSelector = $val[$field];
// If the group has never been found before, add the array
if (!isset($grouped[$fieldSelector]))
$grouped[$fieldSelector] = [];
unset($val[$field]);
$grouped[$fieldSelector][] = $val;
}
}
$this->result->exchangeArray($grouped);
return $this;
}
/**
* Convert the result into an array
*
* @return array
*/
public function toArray(): array
{
// First make sure all data is fetched
$this->allToArray();
// And return a copy
return $this->result->getArrayCopy();
}
/**
* Retrieve an external iterator
* @link https://php.net/manual/en/iteratoraggregate.getiterator.php
* @return Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
* @since 5.0.0
*/
public function getIterator(): Traversable
{
return $this->traversable;
}
private function allToArray(): void
{
// If the input has already been fetched, ignore it
if ($this->fullyFetched)
return;
$result = [];
foreach ($this->raw as $key => $val)
{
// Clear out all numeric keys
foreach ($val as $recKey => $recVal)
if (is_numeric($recKey))
unset($val[$recKey]);
$result[$key] = $val;
}
// Set the variable
$this->result = new ArrayObject($result);
// Afterwards modify the traversable
$this->traversable = $this->result->getIterator();
// Set fullyFetched to true so it doesn't get fetched again
$this->fullyFetched = true;
}
}

View File

@ -0,0 +1,156 @@
<?php
/**
* FuzeWorks Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2019 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Model;
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
use FuzeWorks\Exception\DatabaseException;
interface iDatabaseTableModel
{
/**
* Returns the name of the TableModel.
*
* Usually 'pdo' or 'mongo'.
*
* @return string
*/
public function getName(): string;
/**
* Return the name of the engine used by this TableModel.
*
* Usually 'pdo' or 'mongo'
*
* @return string
*/
public function getEngineName(): string;
/**
* Return the engine used by this TableModel
*
* @return iDatabaseEngine
*/
public function getEngine(): iDatabaseEngine;
/**
* Method invoked by FuzeWorks\Database to setup this tableModel.
*
* Provides the TableModel with the appropriate iDatabaseEngine and the name of the table.
*
* @param iDatabaseEngine $engine
* @param string $tableName
* @return void
* @throws DatabaseException
*/
public function setUp(iDatabaseEngine $engine, string $tableName): void;
/**
* Returns whether the TableModel has been setup yet
*
* @return bool
*/
public function isSetup(): bool;
/**
* Creates data in the model.
*
* @param array $data
* @param array $options
* @return int
* @throws DatabaseException
*/
public function create(array $data, array $options = []): int;
/**
* Returns data from the model in the form of a TableModelResult
*
* @param array $filter
* @param array $options
* @return TableModelResult
* @throws DatabaseException
* @see TableModelResult
*/
public function read(array $filter = [], array $options = []): TableModelResult;
/**
* Updates data in the model
*
* @param array $data
* @param array $filter
* @param array $options
* @return int
* @throws DatabaseException
*/
public function update(array $data, array $filter, array $options = []): int;
/**
* Deletes data from the model
*
* @param array $filter
* @param array $options
* @return int
* @throws DatabaseException
*/
public function delete(array $filter, array $options = []): int;
/**
* Starts a transaction in the model when supported
*
* @return bool
*/
public function transactionStart(): bool;
/**
* Ends a transaction in the model when supported
*
* @return bool
*/
public function transactionEnd(): bool;
/**
* Commits changes in the model when supported
*
* @return bool
*/
public function transactionCommit(): bool;
/**
* Rolls back changes in the modle when supported
*
* @return bool
*/
public function transactionRollback(): bool;
}

View File

@ -1,114 +0,0 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
*
* @version Version 1.2.0
*/
namespace FuzeWorks\TemplateEngine;
/**
* Template Engine that exports all assigned variables as JSON.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class JsonEngine implements TemplateEngine
{
/**
* All the currently assigned variables.
*
* @var array
*/
protected $assigned_variables = array();
/**
* Whether the JSON data should be parsed or left as is.
*
* @var bool true if to be parsed
*/
protected static $string_return = true;
/**
* Whether the JSON data should be parsed or left as is.
*
* @param true if to be parsed
*/
public static function returnAsString($boolean = true)
{
self::$string_return = $boolean;
}
public function setDirectory($directory)
{
return true;
}
public function get($file, $assigned_variables)
{
// First set all the variables
$this->assigned_variables = $assigned_variables;
// First set up the JSON array
$json = array();
// Look up if a file is provided
if (!is_null($file)) {
// Retrieve a file
$string = file_get_contents($file);
$json = json_decode($string, true);
}
// Then assign all variables
$json['data'] = $this->assigned_variables;
// And return it
if (self::$string_return) {
return json_encode($json);
}
return $json;
}
public function getFileExtensions(): array
{
return array('json');
}
public function reset(): bool
{
$this->assigned_variables = array();
self::$string_return = true;
return true;
}
}

View File

@ -1,112 +0,0 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
*
* @version Version 1.2.0
*/
namespace FuzeWorks\TemplateEngine;
use FuzeWorks\Exception\LayoutException;
use FuzeWorks\Core;
use Latte\Engine as Latte;
/**
* Wrapper for the Latte Engine from Nette Framework.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class LatteEngine implements TemplateEngine
{
/**
* Instance of the Latte Engine
*
* @var Latte\Engine The Latte Engine to be used
*/
protected $latte;
/**
* Set the directory of the current template.
*
* @param string $directory Template Directory
* @throws LayoutException
*/
public function setDirectory($directory)
{
if (class_exists('\Latte\Engine', true))
{
// If possible, load Latte\Engine
$this->latte = new Latte;
$this->latte->setTempDirectory(realpath(Core::$tempDir . DS . 'Latte'));
}
else
{
throw new LayoutException("Could not load LatteEngine. Is it installed or Composer not loaded?", 1);
}
}
/**
* Handle and retrieve a template file.
*
* @param string $file Template File
* @param array $assigned_variables All the variables used in this layout
*
* @return string Output of the template
*/
public function get($file, $assigned_variables)
{
return $this->latte->renderToString($file, $assigned_variables);
}
/**
* Retrieve the file extensions that this template engine uses.
*
* @return array All used extensions. eg: array('php')
*/
public function getFileExtensions(): array
{
return array('latte');
}
/**
* Reset the template engine to its default state, so it can be used again clean.
*/
public function reset(): bool
{
// If possible, load Latte\Engine
$this->latte = null;
return true;
}
}

View File

@ -1,94 +0,0 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
*
* @version Version 1.2.0
*/
namespace FuzeWorks\TemplateEngine;
/**
* Simple Template Engine that allows for PHP templates.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class PHPEngine implements TemplateEngine
{
/**
* The currently used directory by the template.
*
* @var string
*/
protected $directory;
/**
* All the currently assigned variables.
*
* @var array
*/
protected $assigned_variables = array();
public function setDirectory($directory)
{
$this->directory = $directory;
}
public function get($file, $assigned_variables)
{
// First set all the variables
$this->assigned_variables = $assigned_variables;
$vars = $this->assigned_variables;
$directory = $this->directory;
// Then run the file
if (!is_null($file)) {
ob_start();
include $file;
return ob_get_clean();
}
}
public function getFileExtensions(): array
{
return array('php');
}
public function reset(): bool
{
$this->directory = null;
$this->assigned_variables = array();
return true;
}
}

View File

@ -1,172 +0,0 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
*
* @version Version 1.2.0
*/
namespace FuzeWorks\TemplateEngine;
use FuzeWorks\Core;
use Smarty;
/**
* Wrapper for the Smarty Template Engine.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
*/
class SmartyEngine implements TemplateEngine
{
/**
* The currently used directory by the template.
*
* @var string
*/
protected $directory;
/**
* All the currently assigned variables.
*
* @var array
*/
protected $assigned_variables = array();
/**
* Instance of the Smarty Template Engine.
*
* @var \Smarty
*/
protected $smartyInstance;
public function setDirectory($directory)
{
$this->directory = $directory;
}
public function get($file, $assigned_variables)
{
// First set all the variables
$this->assigned_variables = $assigned_variables;
// Load Smarty
$this->loadSmarty();
// Set the directory
$this->smartyInstance->setTemplateDir($this->directory);
// Then assign all variables
foreach ($this->assigned_variables as $key => $value) {
$this->smartyInstance->assign($key, $value);
}
// And finally, load the template
return $this->smartyInstance->fetch($file);
}
/**
* Loads a Smarty instance if it is not already loaded.
*/
private function loadSmarty()
{
if (is_null($this->smartyInstance)) {
$this->smartyInstance = new Smarty();
// Then prepare all variables
$this->smartyInstance->setCompileDir(Core::$tempDir . DS . 'Smarty' . DS . 'Compile');
$this->smartyInstance->setCacheDir(Core::$tempDir . DS . 'Smarty');
}
}
public function getFileExtensions(): array
{
return array('tpl');
}
public function reset(): bool
{
$this->smartyInstance = null;
$this->directory = null;
$this->assigned_variables = array();
return true;
}
/**
* Retrieve a value from Smarty.
*
* @param string $name Variable name
*
* @return mixed Variable Value
*
* @throws \FuzeWorks\LayoutException on error
*/
public function __get($name)
{
// First load Smarty
$this->loadSmarty();
return $this->smartyInstance->$name;
}
/**
* Set a variable in Smarty.
*
* @param string $name Variable Name
* @param mixed $value Variable Value
*
* @throws \FuzeWorks\LayoutException on error
*/
public function __set($name, $value)
{
// First load Smarty
$this->loadSmarty();
$this->smartyInstance->$name = $value;
}
/**
* Calls a function in Smarty.
*
* @param string $name Name of the function to be called
* @param Paramaters $params Parameters to be used
*
* @return mixed Function output
*/
public function __call($name, $params)
{
// First load Smarty
$this->loadSmarty();
return call_user_func_array(array($this->smartyInstance, $name), $params);
}
}

View File

@ -0,0 +1,52 @@
<style class="tracy-debug">
#tracy-debug td.nette-DbConnectionPanel-sql { background: white !important }
#tracy-debug .nette-DbConnectionPanel-source { color: #BBB !important }
#tracy-debug .nette-DbConnectionPanel-explain td { white-space: pre }
#tracy-debug .fuzeworks-DbDescriptor th { background: #FDF5CE !important }
</style>
<h1 title="Database">Queries: <?php
echo $results['queryCount'], ($results['queryTimings'] ? sprintf(', time: %0.3f ms', $results['queryTimings'] * 1000) : ''); ?></h1>
<div class="tracy-inner">
<table>
<?php
if (isset($results['queries'])):
foreach ($results['queries'] as $database => $queries): ?>
<tr class='fuzeworks-DbDescriptor'>
<th>Database:</th>
<th><?= htmlSpecialChars($database, ENT_QUOTES, 'UTF-8') ?></th>
<th>#</th>
</tr>
<tr><th>Time&nbsp;ms</th><th>SQL Query</th><th>Rows</th></tr>
<?php foreach ($queries as $query): ?>
<tr>
<td>
<?php if (!empty($query['errors'])): ?>
<span title="<?= htmlSpecialChars(($query['errors']['message'] ?? ''), ENT_IGNORE | ENT_QUOTES, 'UTF-8') ?>">ERROR</span>
<br /><a class="tracy-toggle tracy-collapsed" data-tracy-ref="^tr .nette-DbConnectionPanel-explain">explain</a>
<?php elseif ($query['timings'] !== 0): echo sprintf('%0.3f', $query['timings'] * 1000); endif ?>
</td>
<td class="nette-DbConnectionPanel-sql"><?= htmlSpecialChars($query['query'], ENT_QUOTES, 'UTF-8') ?>
<?php if (!empty($query['errors'])): ?>
<table class="tracy-collapsed nette-DbConnectionPanel-explain">
<tr>
<th>Code</th>
<th>Message</th>
</tr>
<tr>
<td><?= htmlSpecialChars($query['errors']['code'], ENT_NOQUOTES, 'UTF-8') ?></td>
<td><?= htmlSpecialChars(($query['errors']['message'] ?? ''), ENT_NOQUOTES, 'UTF-8') ?></td>
</tr>
</table>
<?php endif ?>
</td>
<td> <?= htmlSpecialChars(var_export($query['data'], true), ENT_QUOTES, 'UTF-8') ?> </td>
</tr>
<?php endforeach;
endforeach; endif; ?>
</table>
<?php if ($results['queryCountProvided'] < $results['queryCount']): ?><p>...and more</p><?php endif ?>
</div>

View File

@ -0,0 +1,19 @@
<?php
if ($results['queryCount'] && !$results['errorsFound'])
{
$color = "#6ba9e6";
}
elseif ($results['queryCount'] && $results['errorsFound'])
{
$color = "#990000";
}
else
{
$color = "#aaa";
}
?>
<span title="Database">
<svg viewBox="0 0 2048 2048"><path fill="<?= $color ?>" d="M1024 896q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"/>
</svg><span class="tracy-label"><?= ($results['queryTimings'] ? sprintf('%0.1fms/', $results['queryTimings'] * 1000) : '') . $results['queryCount'] ?></span>
</span>

View File

@ -1,6 +1,6 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
@ -29,9 +29,9 @@
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
* @since Version 1.1.4
*
* @version Version 1.2.0
* @version Version 1.3.0
*/
use FuzeWorks\Logger;
@ -41,16 +41,10 @@ chdir(dirname(__DIR__));
// Load the FuzeWorks container
$container = require('bootstrap.php');
Logger::disableHandlers();
// Load the test abstract
require_once 'layout/LayoutTestAbstract.php';
// Reset error and exception handlers
restore_error_handler();
restore_exception_handler();
// Set logger template for output in CLI
Logger::setLoggerTemplate('logger_cli');
require_once 'database/DatabaseTestAbstract.php';
// Display all errors
ini_set('display_errors', 1);
@ -59,4 +53,5 @@ error_reporting(E_ALL | E_STRICT);
// Set localhost "remote" IP
isset($_SERVER['REMOTE_ADDR']) OR $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
// Set logger template for output in CLI
Logger::setLoggerTemplate('logger_cli');

View File

@ -1,6 +1,6 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
@ -29,11 +29,14 @@
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
* @since Version 1.1.4
*
* @version Version 1.2.0
* @version Version 1.1.4
*/
use FuzeWorks\DatabaseComponent;
use FuzeWorks\TracyComponent;
require_once(dirname(__DIR__) . '/vendor/autoload.php');
$configurator = new FuzeWorks\Configurator();
@ -44,14 +47,12 @@ $configurator->setLogDirectory(dirname(__FILE__) . '/temp');
// Other values
$configurator->setTimeZone('Europe/Amsterdam');
$configurator->enableDebugMode(true);
$configurator->enableDebugMode()->setDebugAddress('ALL');
// Implement the Layout Component
$configurator->addComponent(new \FuzeWorks\LayoutComponent());
$configurator->addComponent(new DatabaseComponent());
$configurator->addComponent(new TracyComponent());
// Create container
$container = $configurator->createContainer();
// And return the result
return $container;
return $configurator->createContainer();

View File

@ -1,6 +1,6 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
@ -29,15 +29,31 @@
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
* @since Version 1.1.4
*
* @version Version 1.2.0
* @version Version 1.1.4
*/
namespace FuzeWorks;
use FuzeWorks\Factory;
use FuzeWorks\Database;
class Languages
/**
* Class databaseTest.
*
* Core testing suite. Will test databases, querybuilders and frequently used drivers.
*/
class DatabaseTest extends DatabaseTestAbstract
{
}
/**
* @var Database
*/
protected Database $database;
public function setUp(): void
{
$this->database = Factory::getInstance()->database;
}
}

View File

@ -1,6 +1,6 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
* FuzeWorks Framework Database Component.
*
* The FuzeWorks PHP FrameWork
*
@ -29,11 +29,12 @@
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
* @since Version 1.1.4
*
* @version Version 1.2.0
* @version Version 1.1.4
*/
use FuzeWorks\Config;
use PHPUnit\Framework\TestCase;
use FuzeWorks\Events;
use FuzeWorks\Factory;
@ -44,22 +45,28 @@ use FuzeWorks\Core;
*
* Resets core components to their original state
*/
abstract class LayoutTestAbstract extends TestCase
abstract class DatabaseTestAbstract extends TestCase
{
/**
* Remove all listeners before the next test starts.
*
* Reset the layout manager
*/
public function tearDown()
public function tearDown(): void
{
// Clear all events created by tests
Events::$listeners = array();
Events::$listeners = [];
// Reset all config files
Factory::getInstance()->config->discardConfigFiles();
Factory::getInstance('config')->discardConfigFiles();
// Re-enable events, in case they have been disabled
Events::enable();
// Remove Config overrides
Config::$configOverrides = [];
// Remove autoloader
Core::clearAutoloader();
}
}

View File

@ -1,486 +0,0 @@
<?php
/**
* FuzeWorks Framework Layout Template System.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
use FuzeWorks\Factory;
use FuzeWorks\Layout;
use FuzeWorks\Events;
use FuzeWorks\Priority;
/**
* Class LayoutTest.
*
* This test will test the layout manager and the default TemplateEngines
*/
class LayoutTest extends LayoutTestAbstract
{
/**
* @var Factory
*/
protected $factory;
/**
* @var Layout
*/
protected $layout;
public function setUp()
{
// Load the factory first
$this->factory = Factory::getInstance();
$this->layout = Factory::getInstance()->layouts;
$this->layout->reset();
}
/**
* @covers \FuzeWorks\Layout::init
* @covers \FuzeWorks\LayoutComponent::getClasses
*/
public function testComponent()
{
// Load the component
$component = new FuzeWorks\LayoutComponent();
// Prepare container
$configurator = new \FuzeWorks\Configurator();
$configurator->addComponent($component);
$configurator->setTempDirectory(dirname(__DIR__) . '/temp');
$configurator->setLogDirectory(dirname(__DIR__) . '/temp');
// Create container
$container = $configurator->createContainer();
// Init container;
$this->assertTrue(property_exists($container, 'layouts'));
$this->assertInstanceOf('FuzeWorks\Layout', $container->layouts);
}
/**
* @covers \FuzeWorks\Layout::setFile
* @covers \FuzeWorks\Layout::getFile
* @covers \FuzeWorks\Layout::setDirectory
* @covers \FuzeWorks\Layout::getDirectory
*/
public function testFileAndDirectory()
{
// File test
$file = 'test.php';
$this->layout->setFile($file);
$this->assertEquals($file, $this->layout->getFile());
// Directory test
$directory = 'test'.DS.'templates'.DS.'testFileAndDirectory';
$this->layout->addComponentPath($directory);
$this->assertEquals([$directory], $this->layout->getComponentPaths());
}
/**
* @covers \FuzeWorks\Layout::getExtensionFromFile
*/
public function testGetFileExtensions()
{
// Test getting php files
$this->assertEquals('php', $this->layout->getExtensionFromFile('class.test.php'));
$this->assertEquals('php', $this->layout->getExtensionFromFile('class.test.org.php'));
$this->assertEquals('random', $this->layout->getExtensionFromFile('class.test.something.random'));
}
/**
* @depends testGetFileExtensions
* @covers \FuzeWorks\Layout::setFileFromString
* @covers \FuzeWorks\Layout::getFileFromString
*/
public function testGetFilePath()
{
// Extensions to be used in this test
$extensions = array('php', 'json');
// Prepare variables
$directories = [3 => ['test'.DS.'templates'.DS.'testGetFilePath']];
// Basic path
$this->layout->setFileFromString('test', $directories, $extensions);
$this->assertEquals('test'.DS.'templates'.DS.'testGetFilePath'.DS.'layout.test.php', $this->layout->getFile());
// Alternate file extension
$this->layout->setFileFromString('JSON', $directories, $extensions);
$this->assertEquals('test'.DS.'templates'.DS.'testGetFilePath'.DS.'layout.JSON.json', $this->layout->getFile());
// Complex deeper path
$this->layout->setFileFromString('Deeper/test', $directories, $extensions);
$this->assertEquals('test'.DS.'templates'.DS.'testGetFilePath'.DS.'Deeper'.DS.'layout.test.php', $this->layout->getFile());
}
/**
* @depends testGetFilePath
* @expectedException FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::setFileFromString
* @covers \FuzeWorks\Layout::getFileFromString
*/
public function testMalformedPaths()
{
// Extensions to be used in this test
$extensions = array('php', 'json');
$this->layout->setFileFromString('test?\/<>', [3=>['test|?/*<>']], $extensions);
}
/**
* @expectedException FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::setFileFromString
* @covers \FuzeWorks\Layout::getFileFromString
*/
public function testMissingDirectory()
{
// Directory that does not exist
$this->layout->setFileFromString('test', [3=>['test'.DS.'templates'.DS.'doesNotExist']], array('php'));
}
/**
* @expectedException FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::setFileFromString
* @covers \FuzeWorks\Layout::getFileFromString
*/
public function testMissingFile()
{
$this->layout->setFileFromString('test', [3=>['test'.DS.'templates'.DS.'testMissingFile']], array('php'));
}
/**
* @expectedException FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::setFileFromString
* @covers \FuzeWorks\Layout::getFileFromString
*/
public function testUnknownFileExtension()
{
$this->layout->setFileFromString('test', [3=>['test'.DS.'templates'.DS.'testUnknownFileExtension']], array('php'));
}
/**
* @covers \FuzeWorks\Layout::get
*/
public function testLayoutGet()
{
// Directory of these tests
$directories = ['test'.DS.'templates'.DS.'testLayoutGet'];
$this->assertEquals('Retrieved Data', $this->layout->get('test', $directories));
}
/**
* @covers \FuzeWorks\Layout::get
*/
public function testLayoutGetRepeat()
{
$directories = ['test'.DS.'templates'.DS.'testLayoutGetRepeat'];
$this->assertEquals('First Data', $this->layout->get('first', $directories));
$this->assertEquals('Second Data', $this->layout->get('second', $directories));
}
/**
* @covers \FuzeWorks\Layout::get
* @covers \FuzeWorks\Event\LayoutLoadEvent::init
*/
public function testLayoutGetCancelledEvent()
{
$directories = ['test'.DS.'templates'.DS.'testLayoutGetCancelledEvent'];
Events::addListener(function($event){
$event->setCancelled(true);
}, 'layoutLoadEvent', Priority::NORMAL);
$this->assertEquals('cancelled', $this->layout->get('test', $directories));
}
/**
* @expectedException FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::get
* @covers \FuzeWorks\Event\LayoutLoadEvent::init
*/
public function testLayoutGetEventWrongFile()
{
$directories = ['test'.DS.'templates'.DS.'testLayoutGetEventWrongFile'];
Events::addListener(function($event){
$event->file = 'does_not_exist';
}, 'layoutLoadEvent', Priority::NORMAL);
$this->layout->get('test', $directories);
}
/**
* @covers \FuzeWorks\Layout::display
* @covers \FuzeWorks\Event\LayoutDisplayEvent::init
*/
public function testLayoutDisplayEventAndDisplay()
{
// Directory of these tests
$directories = ['test'.DS.'templates'.DS.'testLayoutGet'];
Events::addListener(function($event){
$this->assertEquals('Retrieved Data', $event->contents);
}, 'layoutDisplayEvent', Priority::NORMAL);
ob_start();
$this->layout->display('test', $directories);
$this->assertEquals('Retrieved Data', ob_get_contents());
ob_end_clean();
}
/**
* @covers \FuzeWorks\Layout::reset
* @covers \FuzeWorks\Layout::setTitle
* @covers \FuzeWorks\Layout::getTitle
*/
public function testReset()
{
$this->layout->setDirectories([3=>['test'.DS.'templates'.DS.'testLayoutGet']]);
// First the the variables
$this->layout->setTitle('Test Title');
// Test if they are actually set
$this->assertEquals('Test Title', $this->layout->getTitle());
$this->assertEquals(['test'.DS.'templates'.DS.'testLayoutGet'], $this->layout->getComponentPaths());
// Reset the layout system
$this->layout->reset();
// Test for default values
$this->assertEquals(['test'.DS.'templates'.DS.'testLayoutGet'], $this->layout->getComponentPaths());
}
/**
* @covers \FuzeWorks\Layout::getEngineFromExtension
*/
public function testGetEngineFromExtension()
{
$this->layout->loadTemplateEngines();
// Test all the default engines
$this->assertInstanceOf('FuzeWorks\TemplateEngine\PHPEngine', $this->layout->getEngineFromExtension('php'));
$this->assertInstanceOf('FuzeWorks\TemplateEngine\JsonEngine', $this->layout->getEngineFromExtension('json'));
$this->assertInstanceOf('FuzeWorks\TemplateEngine\SmartyEngine', $this->layout->getEngineFromExtension('tpl'));
}
/**
* @depends testGetEngineFromExtension
* @expectedException FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::getEngineFromExtension
*/
public function testGetEngineFromExtensionFail()
{
$this->layout->getEngineFromExtension('faulty');
}
/**
* @covers \FuzeWorks\Layout::loadTemplateEngines
*/
public function testLoadTemplateEngines()
{
// Load first try
$this->assertTrue($this->layout->loadTemplateEngines());
// Try second time
$this->assertFalse($this->layout->loadTemplateEngines());
// Reset
$this->layout->reset();
$this->assertTrue($this->layout->loadTemplateEngines());
}
/**
* @covers \FuzeWorks\Layout::loadTemplateEngines
* @expectedException \FuzeWorks\Exception\LayoutException
*/
public function testLoadLoadEngineEvent()
{
Events::addListener(function($event){
$this->assertInstanceOf('\FuzeWorks\Event\NotifierEvent', $event);
throw new \FuzeWorks\Exception\EventException('Forcing failure in loadTemplateEngines()');
}, 'layoutLoadEngineEvent', Priority::NORMAL);
$this->layout->loadTemplateEngines();
}
/**
* @depends testGetEngineFromExtension
* @covers \FuzeWorks\Layout::registerEngine
*/
public function testCustomEngine()
{
// Create the engine
$mock = $this->getMockBuilder('FuzeWorks\TemplateEngine\TemplateEngine')->getMock();
// Add the methods
$mock->method('get')->willReturn('output');
// And listen for usage
$mock->expects($this->once())->method('get')->with('test'.DS.'templates'.DS.'testCustomEngine'.DS.'layout.test.test');
// Register the engine
$this->layout->registerEngine($mock, 'Custom', array('test'));
// And run the engine
$this->assertEquals('output', $this->layout->get('test', ['test'.DS.'templates'.DS.'testCustomEngine']));
}
/**
* @depends testCustomEngine
* @expectedException \FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::registerEngine
*/
public function testExistingCustomEngine()
{
// Create mock
$mock = $this->getMockBuilder('FuzeWorks\TemplateEngine\TemplateEngine')->getMock();
$mock->method('get')->willReturn('output');
// And register
$this->assertTrue($this->layout->registerEngine($mock, 'Custom', ['test']));
// And re-register
$this->layout->registerEngine($mock, 'Custom', ['othertest']);
}
/**
* @depends testCustomEngine
* @expectedException \FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::registerEngine
*/
public function testCustomEngineWithExistingExtensions()
{
// Create mock
$mock = $this->getMockBuilder('FuzeWorks\TemplateEngine\TemplateEngine')->getMock();
$mock->method('get')->willReturn('output');
// Register initial
$this->assertTrue($this->layout->registerEngine($mock, 'Custom', ['test']));
// Register failing
$this->layout->registerEngine($mock, 'other', ['test']);
}
/**
* @depends testCustomEngine
* @covers \FuzeWorks\Layout::setEngine
* @covers \FuzeWorks\Layout::getEngine
*/
public function testSetEngine()
{
// Create mocks
$mock = $this->getMockBuilder('FuzeWorks\TemplateEngine\TemplateEngine')->getMock();
$mock2 = $this->getMockBuilder('FuzeWorks\TemplateEngine\TemplateEngine')->getMock();
$mock->method('get')->willReturn('output');
$mock2->method('get')->willReturn('output2');
// Register custom engine
$this->assertTrue($this->layout->registerEngine($mock, 'custom', ['test']));
$this->assertTrue($this->layout->registerEngine($mock2, 'custom2', ['test2']));
// Test getEngine
$this->assertInstanceOf(get_class($mock), $this->layout->getEngine('custom'));
$this->assertInstanceOf(get_class($mock2), $this->layout->getEngine('custom2'));
// Test setEngine1
$this->assertTrue($this->layout->setEngine('custom'));
$this->assertTrue($this->layout->setEngine('custom2'));
}
/**
* @depends testSetEngine
* @expectedException \FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::setEngine
*/
public function testSetInvalidEngine()
{
$this->layout->setEngine('invalid');
}
/**
* @depends testSetEngine
* @expectedException \FuzeWorks\Exception\LayoutException
* @covers \FuzeWorks\Layout::getEngine
*/
public function testGetInvalidEngine()
{
$this->layout->getEngine('invalid');
}
/**
* @covers \FuzeWorks\Layout::registerEngine
*/
public function testEnginesLoadLayout()
{
// Directory of these tests
$directories = ['test'.DS.'templates'.DS.'testEngines'];
// First the PHP Engine
$this->assertEquals('PHP Template Check', $this->layout->get('php', $directories));
$this->layout->reset();
// Then the JSON Engine
$this->assertEquals('JSON Template Check', json_decode($this->layout->get('json', $directories), true)[0]);
$this->layout->reset();
// And the Smarty Engine
$this->assertEquals('Smarty Template Check', $this->layout->get('smarty', $directories));
}
/**
* @covers \FuzeWorks\Layout::assign
*/
public function testEngineVariables()
{
// Directory of these tests
$directories = ['test'.DS.'templates'.DS.'testEngineVariables'];
// First the PHP Engine
$this->layout->assign('key', 'value');
$this->assertEquals('value', $this->layout->get('php', $directories));
$this->layout->reset();
// Then the JSON Engine
$this->layout->assign('key', 'value');
$this->assertEquals('value', json_decode($this->layout->get('json', $directories), true)['data']['key']);
$this->layout->reset();
// And the Smarty Engine
$this->layout->assign('key', 'value');
$this->assertEquals('value', $this->layout->get('smarty', $directories));
}
}
class MockEngine {
}

View File

@ -1,34 +1,19 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
bootstrap="autoload.php"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
colors="false">
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="autoload.php" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnSkipped="false" colors="false">
<coverage processUncoveredFiles="false">
<include>
<directory suffix=".php">../</directory>
</include>
<exclude>
<directory suffix=".php">../vendor/</directory>
<directory suffix=".php">../test/</directory>
<directory suffix=".php">../src/Layout/</directory>
<directory suffix=".php">../src/Config/</directory>
</exclude>
</coverage>
<testsuites>
<testsuite name="Core Functionality">
<testsuite name="Core Suite">
<directory>./</directory>
</testsuite>
</testsuites>
<logging>
<log type="json" target="../build/phpunit/logfile.json"/>
<log type="junit" target="../build/phpunit/logfile.xml"/>
<log type="testdox-html" target="../build/phpunit/testdox.html"/>
<log type="testdox-text" target="../build/phpunit/testdox.txt"/>
</logging>
<filter>
<whitelist processUncoveredFilesFromWhitelist="false">
<directory suffix=".php">../</directory>
<exclude>
<directory suffix=".php">../vendor/</directory>
<directory suffix=".php">../test/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -1,2 +0,0 @@
<?php
echo $vars['key'];

View File

@ -1 +0,0 @@
{$key}

View File

@ -1 +0,0 @@
["JSON Template Check"]

View File

@ -1 +0,0 @@
<?php echo 'PHP Template Check';

View File

@ -1 +0,0 @@
Smarty Template Check

View File

@ -1 +0,0 @@
Retrieved Data

View File

@ -1 +0,0 @@
First Data

View File

@ -1 +0,0 @@
Second Data