PHPunit Global Variable returns NULL - php

I'm new to php Unit test, but I could installed it and make it work (PHP 7.1.2 / phpUnit 6.1.1 on a Mac / MAMP system). But I now I can't let it test my app. Probably due to a strange class architectural hierarchy.
In my autoload.php I load my classes like that:
/* get timing functions */
$th = new TimingHelper();
/* Get basic functions */
$basic = new basic();
/* Get App data (param: fake localhost true / false) */
$App = new App();
/* Start DB Connection */
$db = new db(0);
/* Get usr data (param: fake user mode true / false, user id) */
$usr = new User();
There are even more classes... ;)
Now inside the User Class I get the previous classes like that:
class User {
/* get DB connection */
private $db, $App, $tr, $th, $basic;
public function __construct() {
/* get DB connection */
Global $db, $App, $tr, $th, $basic;
$this->db = $db;
$this->App = $App;
$this->tr = $tr;
$this->th = $th;
$this->basic = $basic;
/* Start timing */
$this->th->start();
}
}
That's probably not way one should do it, but it works ;)
But now if I want to use phpUnit, I heard it ignores global objects, so
var_dump($this->th);
is NULL.
Is there no way, that I can use phpUnit with my class system? If not, what do I need to change?
I tried to extend each class, but then my app was SUPER slow...

Related

Symfony 4: Keep SQLite PDO connection in test + controller + twig extensions

My situation
I have a Symfony 4.2 project with the following structure:
src
Controller
Service
Twig Extensions
Templates
Tests
I use a database class, which internally creates a PDO connection. If i run my tests with PHPUnit, my database class has to switch from mysql to sqlite. Everything works fine here.
My problem
I can not keep the once created Database instance, when running just one test. Symfony seems to recreate it during the test: when inside a Twig template which uses a Twig extension. Because the database class uses
new \PDO('sqlite::memory:');
it loses created tables and therefore the test fails. I am aware of, that the Database instance (with PDO reference) gets reseted after each test, but in my situation i only have one test. How can i ensure, that it re-uses the Database instance?
Here the related code
InstanceExtension class provides the function title, which is used in a Twig template and has to access the database.
<?php
namespace App\TwigExtension;
use App\Service\Database;
use Twig\TwigFunction;
use Twig\Extension\AbstractExtension;
class InstanceExtension extends AbstractExtension
{
protected $db;
/**
* #param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* Tries to return the title/name for a given ID.
*
* #param string $subject
* #param string|array $tables
* #param string $lang Default is 'de'
*
* #return string label or id, if no title/name was found
*/
public function title(string $subject, $tables, string $lang = 'de'): string
{
return $this->db->get_instance_title($subject, $tables, $lang);
}
}
In my services.yaml the Database class is set to public (which should enable reusing it, doesn't it?):
App\Service\Database:
public: true
Database class
Here is the part of the Database class which initializes the PDO connection. Production code, which uses MySQL instead, removed:
<?php
declare(strict_types=1);
namespace App\Service;
class Database
{
/**
* #param Config $app_config
*
* #throws \Exception
*/
public function __construct(Config $app_config)
{
global $sqlite_pdo;
try {
// non test
// ..
// test environment
} else {
$this->db_engine = 'sqlite';
// workaround to ensure we have the same PDO instance everytime we use the Database instance
// if we dont use it, in Twig extensions a new Database instance is created with a new SQLite
// database, which is empty.
if (null == $sqlite_pdo) {
$pdo = new \PDO('sqlite::memory:');
$sqlite_pdo = $pdo;
} else {
$pdo = $sqlite_pdo;
}
}
} catch (\PDOException $e) {
if (\strpos((string) $e->getMessage(), 'could not find driver') !== false) {
throw new \Exception(
'Could not create a PDO connection. Is the driver installed/enabled?'
);
}
if (\strpos((string) $e->getMessage(), 'unknown database') !== false) {
throw new \Exception(
'Could not create a PDO connection. Check that your database exists.'
);
}
// Don't leak credentials directly if we can.
throw new \Exception(
'Could not create a PDO connection. Please check your username and password.'
);
}
if ('mysql' == $this->db_engine) {
// ...
}
// default fetch mode is associative
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
// everything odd will be handled as exception
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$this->db = $pdo;
}
// ...
}
One test looks kinda like:
<?php
class SalesChannelControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
// init Database class, using SQLite
$this->init_db();
// further setup function calls
// SalesChannelController is a child of
// Symfony\Bundle\FrameworkBundle\Controller\AbstractController
$this->fixture = new SalesChannelController(
$this->db
// ...
);
}
/**
* Returns a ready-to-use instance of the database. Default adapter is SQLite.
*
* #return Database
*/
protected function init_db(): Database
{
// config parameter just contains DB credentials
$this->db = new Database($this->config);
}
public function test_introduction_action()
{
// preparations
/*
* run action
*
* which renders a Twig template, creates a Response and returns it.
*/
$response = $this->fixture->introduction_action($this->request, $this->session, 'sales_channel1');
/*
* check response
*/
$this->assertEquals(200, $response->getStatusCode());
}
}
My current workaround is to store the PDO instance in a global variable and re-use it, if required.
<?php
global $sqlite_pdo;
// ...
// inside Database class, when initializing the PDO connection
if (null == $sqlite_pdo) {
$pdo = new \PDO('sqlite::memory:');
$sqlite_pdo = $pdo;
} else {
$pdo = $sqlite_pdo;
}
If you need more info, please tell me. Thanks for your time and help in advance!

How do I fetch the PHP DI container?

How do I load a database container using PHP DI?
This is one of the variations I have tried up until now.
Settings.php
<?php
use MyApp\Core\Database;
use MyApp\Models\SystemUser;
return [
'Database' => new Database(),
'SystemUser' => new SystemUser()
];
init.php
$containerBuilder = new \DI\ContainerBuilder();
$containerBuilder->addDefinitions('Settings.php');
$container = $containerBuilder->build();
SystemUserDetails.php
<?php
namespace MyApp\Models\SystemUser;
use MyApp\Core\Database;
use MyApp\Core\Config;
use MyApp\Helpers\Session;
/**
*
* System User Details Class
*
*/
class SystemUserDetails
{
/*=================================
= Variables =
=================================*/
private $db;
/*===============================
= Methods =
================================*/
/**
*
* Construct
*
*/
public function __construct(Database $db)
{
# Get database instance
// $this->db = Database::getInstance();
$this->db = $db;
}
/**
Too few arguments to function MyApp\Models\SystemUser\SystemUserDetails::__construct(), 0 passed in /www/myapp/models/SystemUser.php on line 54 and exactly 1 expected
File: /www/myapp/models/SystemUser/SystemUserDetails.php
Shouldn't the database get loaded automatically?
Trace:
Currrently, My main index.php file extends init.php which is the file where it create the container (pasted code part in the post).
Then I call the App class, which fetches the URL(mysite.com/login/user_login) and instantiate a new controller class and run the mentioned method, in this case, it's the first page - MyApp/Contollers/Login.php.
The user_login method fetches the credentials, validate them, and if they are valid, uses the SystemUser object to login.
SystemUser class:
namespace MyApp\Models;
class SystemUser
{
public $id;
# #obj SystemUser profile information (fullname, email, last_login, profile picture, etc')
protected $systemUserDetatils;
public function __construct($systemUserId = NULL)
{
# Create systemUserDedatils obj
$this->systemUserDetatils = new \MyApp\Models\SystemUser\SystemUserDetails();
# If system_user passed
if ( $systemUserId ) {
# Set system user ID
$this->id = $systemUserId;
# Get SysUser data
$this->systemUserDetatils->get($this);
} else {
# Check for sysUser id in the session:
$systemUserId = $this->systemUserDetatils->getUserFromSession();
# Get user data from session
if ( $systemUserId ) {
# Set system user ID
$this->id = $systemUserId;
# Get SysUser data
$this->systemUserDetatils->get($this);
}
}
}
}
PHP-DI is working correctly.
In your SystemUser class you are doing:
$this->systemUserDetatils = new \MyApp\Models\SystemUser\SystemUserDetails();
The constructor for SystemUserDetails requires a Database object, which you are not passing.
By calling new directly, you are not using PHP-DI. By doing this you are hiding the dependency, which is exactly what you are supposedly trying to avoid if you want to use a dependency injection system.
If SystemUser depends ("needs") SystemUserDetails, the dependency should be explicit (e.g. declared in its constructor).
Furthermore: You do not need a definitions file for a system like this. And the definitions file you show in your question doesn't follow the best practices recommended by PHP-DI.
Your design is far from perfect, and I'm not sure of your end-goals, but if you did something like this, it could work:
<?php
// src/Database.php
class Database {
public function getDb() : string {
return 'veryDb';
}
}
<?php
// src/SystemUserDetails.php
class SystemUserDetails {
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function getDetails() {
return "Got details using " . $this->db->getDb() . '.';
}
}
<?php
// src/SystemUser.php
class SystemUser {
protected $details;
public function __construct(SystemUserDetails $details, $userId=null) {
$this->details = $details;
}
public function getUser() {
return "Found User. " .$this->details->getDetails();
}
}
<?php
//init.php
require_once('vendor/autoload.php');
// build the container. notice I do not use a definition file.
$containerBuilder = new \DI\ContainerBuilder();
$container = $containerBuilder->build();
// get SystemUser instance from the container.
$userInstance = $container->get('SystemUser');
echo $userInstance->getUser(), "\n";
Which results in:
Found User. Got details using veryDb.

Could someone give a perfect example of a basic "user" object system, using PDO?

What I'd like is to see the ideal framework for a system which has a group of objects (ie User) whereby the data is contained in a database. I've been advised to have a User class and a UserMapper class and this is my understanding of how it should look:
user.class.php
/* The class for constructing any user's information
*/
class User {
protected $userId, $email, $userGroup;
protected function getEmail() {
return $this->email;
}
protected function getUserId() {
return $this->userId;
}
public function __construct($userId, $email, $userGroup) {
$this->userId = $userId;
$this->email = $email;
$this->userGroup = $userGroup;
}
}
class UserMapper {
// database connection
private $db;
public function __construct($db)
{
$this->db = $db;
}
public function findByUserId ($userId) {
$userObject = new User();
$q = $this->db->prepare("SELECT userId, email, userGroup FROM user WHERE userId = :userId");
$q->bindValue(":userId", $id);
$q->setFetchMode( PDO::FETCH_INTO, $userObject);
$q->execute();
$q->fetch(PDO::FETCH_INTO);
return $userObject;
}
}
?>
main.php
<?php
include user.class.php;
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));
$getUser = new UserMapper($dbh);
$user = $getUser->findByUserId(41);
echo $user->getEmail();
?>
But this seems a bit messy in terms of the main.php side. Can I not make one PDO object and have that defined in all of my scripts? As well as a UserMapper object? Or do every time I want to get a user from the database do I need to make a NEW userMapper object, then do findByUserId (as above). Or is there a simpler way to doing this?
If I wanted to call a UserGroup object within the class User, how would I do this? (This would also need to connect to the database through PDO). To do the following seems messy:
<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));
$getUserGroup = new UserGroupMapper($dbh);
$userGroup = $getUserGroupMapper->findByUserId($this->userGroup);
?>
one thing that i can think of is making this class a singleton, and create the $user above the declaration of the class, so whenever you include this class you'll have that user object.
Can I not make one PDO object and have that defined in all of my
scripts? As well as a UserMapper object?
You're actually looking for a Front Controller.
That is, in order to avoid the same instantiation of the same classes, you should have prepared them. Most people usually do this in bootstrap.php, that "tweaks" all required dependencies.
But a front controller implementation also includes a dispatcher and a router. I won't go deep into this, but focus on the problem you're trying to solve instead.
Factory pattern
It basically abstracts instantiation logic. The benefits are: 1) you can delay object instantiation 2) You avoid global state, which is bad for unit-testing. The simplified version of it would look like as:
class UserFactory
{
private $pdo;
private $cache = array();
public function __construct($pdo)
{
$this->pdo = $pdo;
}
public function build($mapper)
{
if (isset($this->cache[$mapper])) {
return $this->cache[$mapper];
} else {
// Inject an instance into a mapper
$instance = new $mapper($this->pdo);
// Save the state into a cache
$this->cache[get_class($instance)] = $instance;
return $instance;
}
}
}
And finally
A very simplified version of bootstrap-er would look like as,
<?php
/* File : bootstrap.php */
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));
// Include here UserFactory class
$userFactory = new UserFactory($dbh);
// Its kinda ready to be used
You would simply include in all scripts that need to access Users
<?php
/* File: main.php */
include(__DIR__ . '/bootstrap.php');
$getUser = $userFactory->build('UserMapper');
$user = $getUser->findByUserId(41);
echo $user->getEmail();
You need to use FETCH_CLASS instead and you don't need a userMapper just extend PDO then set the right fetch mode all in one class.
Don't forget to make the class definition available or use an autoloader
$this->statement->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE,"className");
FETCH_PROPS_LATE is there to get the constructor fired first in your class But you don't need a constructor in your case so just lose it. If you decide to keep it though than you should take a look here first.
Hope this helps good luck

how to initialize my ldap connection like a doctrine connection in the bootstrap.php file in zend-framework

i want to use my ldap server as a DB. then i will create persistence classes in the models directory that will extend to Zend_Ldap so that i won't have to write all the CRUD operations but how can i initialize the ldap connection in the bootstrap.php file
for e.g. a database connection using doctrine can be initialized like this, i want to do the same for ldap connection
protected function _initDoctrine()
{
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Doctrine');
$this->getApplication()->getAutoloader()
->pushAutoloader(array('Doctrine', 'autoload'));
spl_autoload_register(array('Doctrine', 'modelsAutoload'));
$manager = Doctrine_Manager::getInstance();
$doctrineConfig = $this->getOption('doctrine');
$manager->setAttribute(Doctrine::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
$manager->setAttribute(Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, true);
Doctrine_Core::loadModels($doctrineConfig['models_path']);
$conn = Doctrine_Manager::connection($doctrineConfig['dsn'],'doctrine');
$conn->setAttribute(Doctrine::ATTR_USE_NATIVE_ENUM, true);
return $conn;
}
but now what i want to do is if i have some thing like this(below) in the models folder
class Application_Model_entry extends Zend_Ldap_Node_collection {
public static function getInstance() {
$options = Zend_Registry::get('config')->ldap;//i want to avoid this line
$ldap = new Zend_Ldap($options); // i want to avoid this line
$dn = $ldap->getBaseDn(); // i want to avoid this line
$a = new Zend_Ldap_Node_Collection(new Zend_Ldap_Collection_Iterator_Default($ldap,'email')); //also this one
return $a;//where $a is an instance of an LDAP entry(node)
}
then in the controller i want to do some thing like a db
$ent = new Application_Model_entry();
$ent->email = "xyz#abc.xom";
...
$ent->save();
$ent->update();
how can i initialize the ldap connection and access it in the models so that this could be possible
You can do
protected function _initLdap()
{
$ldap = new Zend_Ldap(/*... your configuration ...*/);
return $ldap;
}
But there is no such thing as a default LDAP connection, so you have to retrieve the LDAP connection object from your bootstrap's resources. Some helper class may, well, help.
By the way your model should not extend Zend_Ldap - at least not for the reason you want to do this. You could e.g. extend Zend_Ldap_Node which is a representation of a single LDAP entry, whilst Zend_Ldap is a representation of the LDAP connection and the LDAP server you're talking to.
EDIT:
class Application_Ldap
{
public static function getLdap()
{
/* return LDAP connection from bootstrap */
}
public static function newEntry($name)
{
$dn = $name; // build DN from given entity name
$node = Zend_Ldap_Node::create($dn);
$node->attachLdap(self::getLdap());
return $node;
}
public static function loadEntry($name)
{
$dn = $name; // build DN from given entity name
$node = Zend_Ldap_Node::fromLdap($dn, self::getLdap());
return $node
}
}
Be advised: this is not really a state-of-the-art model, but just a simple solution to your problem (if I understood it correctly). It allows you to do the following in your application logic:
$newOne = Application_Ldap::newEntry('new-one');
$newOne->email = "xyz#abc.xom";
$newOne->update();
$oldOne = Application_Ldap::loadEntry('old-one');
$oldOne->email = "abc#abc.xom";
$oldOne->update();

How do you manage database connections in php?

So recently I've really started to use php actively, and I need some insights on different ways to use database connections.
At first I just used the simple mysql_connect():
<?php
$connection = mysql_connect(DB_HOST, DB_USER, DB_PASS) or die(mysql_error());
mysql_select_db(DB_DB, $connection);
?>
After a while I created a database class which I started to include and initialize in every file - something like this:
<?php
class MySQL_DB {
var $connection;
function MySQL_DB(){
$this->connection = mysql_connect(DB_HOST, DB_USER, DB_PASS) or die(mysql_error());
mysql_select_db(DB_DB, $this->connection);
}
function query($q){
$res = mysql_query($q, $this->connection) or die(mysql_error());
return $res;
}
}
$database = New MySQL_DB;
?>
And this is what I'm using at the time - and it's working fine - but there are always ways to improve.
So my question to you is how do you manage your database connections?
Do you use classes?
What does your classes contain (just
the connection or even functions?)
What practices do you recommend?
I recommend to use PDO. Don't reinvent the weel. It's a nice OO-interface to many database engines.
Additionally I create a small function which just inititializes PDO object. So all connection settings can be changed in one place.
Your current approach is pretty standard, and works well. I used it for a long time. It's true that modules like PDO provide base functionality like this now, which is very nice as well and can get you away from problems with home-brew code.
However, I've taken the connection management one step further. If you get into a complex application, you might get into a situation where you have multiple databases, or heavy database use. Including a single database connection file and having a global $database variable becomes unwieldy for multiple databases, and it's unnecessary for application requests that might not need a database connection. Remember, connecting to the database is expensive.
What I've done is create a singleton DatabaseManager class that handles the database object for me, and makes sure multiple connections to a given DB don't get instantiated. Instead of initializing a new database object at the top of your app, you simply call on the DatabaseManager every time you need the object.
$db = DatabaseManager::getDatabase();
Here's an example class that I had whipped up for a CodeIgniter project. You can see in the getDatabase() function it simply loads CodeIgniter's default database object, which you would substitute for your own class (and run the connection routine for it) if you weren't using CI. This is a pretty simplistic management class, and could be extended to manage multiple connections to different databases fairly easily.
<?php
/**
* Implements the Singleton pattern to prevent multiple instantiations and connections
* to the application database.
*
*/
class Database_manager
{
private static $instance;
public $db;
/**
* Constructor function is declared private to prevent instantiation.
*
*/
protected function __construct()
{
parent::__construct();
}
/**
* Returns an instance of a Database_manager.
*
* #return object Database_manager object
*/
public static function getInstance()
{
if (self::$instance == null) {
$className = __CLASS__;
self::$instance = new $className();
}
return self::$instance;
}
public static function getDatabase()
{
$instance = self::getInstance();
if ($instance->db == null) {
//utilize CodeIgniter's database loader
$instance->db = $instance->load->database('',true);
if (! is_object($instance->db)) throw new Exception("Could not load database.");
}
return $instance->db;
}
}
Perhaps the most common advantage I get out of using this style of connection management is when I have to take down an application for database maintenance. By not instantiating a database connection until I need it, I can easily put up a "maintenance in progress" message on a site (short circuiting normal MVC dispatching), and not worry about requests to the application opening a DB connection while maintenance is in progress.
Usage of classes are the way to go to increase customized re-usability.
Bring in all generic implementations into the class. You are on the right track.
This website has the following clean approach.
This website link is no longer present. Archive Link.
class connection {
// Possible Modules are as follows:
// DBX_MYSQL, DBX_ODBC, DBX_PGSQL, DBX_MSSQL, DBX_FBSQL, DBX_SYBASECT, DBX_OCI8, DBX_SQLITE
private $module = DBX_MYSQL;
private $host = "localhost";
private $database = "test";
private $username = "testuser";
private $password = "testpass";
private $link;
private $result;
public $sql;
function __construct($database=""){
if (!empty($database)){ $this->database = $database; }
$this->link = dbx_connect($this->module,$this->host,$this->database,$this->username,$this->password);
return $this->link; // returns false if connection could not be made.
}
function query($sql){
if (!empty($sql)){
$this->sql = $sql;
$this->result = dbx_query($this->link,$sql,DBX_RESULT_UNBUFFERED);
return $this->result;
}else{
return false;
}
}
function fetch($result=""){
if (empty($result)){ $result = $this->result; }
return dbx_fetch_row($result);
}
function __destruct(){
dbx_close($this->link);
}
}
In your database manager example, you did not define a parent for your class.
Therefore, invoking parent::__constructor() yields an exception,
and also, you cannot use the load property of code ignitor.
Which class did you use as an extension for your DatabaseManager?
Since i do not know where you placed your databasemanager code, nor which class you used as its parent, i circumvented the exceptions by making the getDatabase() method receive an input parameter which i called $loader.
Normally, this $loader object will be the model class requiring access to a database.
public static function getDatabase($loader)
{
$instance = self::getInstance();
if ($instance->db == null) {
//utilize CodeIgniter's database loader
$instance->db = $loader->load->database('default',true);
if (! is_object($instance->db)) throw new Exception("Could not load database.");
}
return $instance->db;
}
Best regards.

Categories