Nice Syntaxe for creating new object from method - php

There is a shortcut method to create an object from a method that return a string?
For the moment, I used that :
class MyClass {
/**
* #return string
*/
public function getEntityName() {
return 'myEntityName';
}
}
$myClassInstance = new MyClass();
// Need to get string
$entityName = $myclassInstance->getEntityName();
// And after I can instantiate it
$entity = new $entityName();

There is short-cut syntax for getting the string but not for creating the object from the string to date in PHP. See the following code in which I also include a 'myEntityName' class:
<?php
class myEntityName {
public function __construct(){
echo "Greetings from " . __CLASS__,"\n";
}
}
class MyClass {
/**
* #return string
*/
public function getEntityName() {
return 'myEntityName';
}
}
$entityName = ( new MyClass() )->getEntityName();
$entity = new $entityName();
With one line the code instantiates a MyClass object and executes its getEntityName method which returns the string $entityName. Interestingly, if I replace my one-liner with the following it fails in all versions of PHP except for the HipHop Virtual Machine (hhvm-3.0.1 - 3.4.0):
$entityName = new ( ( new MyClass() )->getEntityName() );

Related

PHP Reflection -- is this a bug or expected behavior?

I am creating a class that uses ReflectionProperty and am getting strange results.
Essentially, calling $reflectionProperty->getType()->getName() is returning the value of some entirely unrelated array. It does this under seemingly random conditions.
See below:
// this will be used as a type hinted property.
class DummyClass {
public function __construct($arr) {}
}
// a class that sets its property values reflectively
class BaseClass {
/** #var ReflectionProperty[] */
private static $publicProps = [];
/**
* Gets public ReflectionProperties of the concrete class, and caches them
* so we do not need to perform reflection again for this concrete class.
*
* #return ReflectionProperty[]
* #throws ReflectionException
*/
private function getPublicProps(){
if (!static::$publicProps) {
$concreteClass = get_class($this);
static::$publicProps = (new ReflectionClass($concreteClass))
->getProperties(ReflectionProperty::IS_PUBLIC);
}
return static::$publicProps;
}
/**
* For each public property in this class set value to the corresponding value from $propArr.
*
* #param $propArr
* #throws ReflectionException
*/
public function __construct($propArr) {
$concreteClass = get_class($this);
echo "Creating new instance of $concreteClass<br>";
foreach ($this->getPublicProps() as $prop) {
// get which property to set, its class, and value to pass to constructor
$propName = $prop->getName();
$propClass = $prop->getType()->getName();
$propValue = $propArr[$propName];
$propValueStr = var_export($propValue, true);
// print out what we are about to do, and assert $propClass is correct.
echo "---Setting: ->$propName = new $propClass($propValueStr)<br>";
assert($propClass === "DummyClass", "$propClass !== DummyClass");
// create the instance and assign it
$refClass = new ReflectionClass($propClass);
$this->$propName = $refClass->newInstanceArgs([$propValue]);
}
}
}
// a concrete implementation of the above class, with only 1 type hinted property.
class ConcreteClass extends BaseClass {
public DummyClass $prop1;
}
// should create an instance of ConcreteClass
// with ->prop1 = new DummyClass(["foo"=>"abc123"])
$testArr1 = [
"prop1" => ["foo" => "abc123"]
];
// should create an instance of ConcreteClass
// with ->prop1 = new DummyClass(["boo"=>"abc123def456"])
$testArr2 = [
"prop1" => ["boo" => "abc123def456"]
];
$tc1 = new ConcreteClass($testArr1);
echo "Created TestClass1...<br><br>";
$tc2 = new ConcreteClass($testArr2);
echo "Created TestClass2...<br><br>";
die;
The results:
Creating new instance of ConcreteClass
Setting: ->prop1 = new DummyClass(array ( 'foo' => 'abc123', ))
Created TestClass1...
Creating new instance of ConcreteClass
Setting: ->prop1 = new abc123def456(array ( 'boo' => 'abc123def456', ))
Error: assert(): abc123def456 !== DummyClass failed
Notice that the value of $propClass is abc123def456 -- how did that happen?
More Weirdness
Change the value of "abc123def456" to "12345678" and it will work.
Change the value of "abc123def456" to "123456789" and it will not work.
Omit the var_export(), and it will work. (Though, it may still break in other cases).
My gut tells me this is a PHP bug, but I might be doing something wrong, and/or this may be documented somewhere. I would like some clarification, because as of right now my only reliable solution is to not cache the reflected $publicProps. This results in an unnecessary call to ReflectionClass->getProperties() every single time I create a new ConcreteClass, which I'd like to avoid.
Turns out this was a bug in PHP: https://bugs.php.net/bug.php?id=79820
Minimal reproduction here: https://3v4l.org/kchfm
Fixed in 7.4.9: https://www.php.net/ChangeLog-7.php#7.4.9

What is the best practice in PHP to create multiple objects if you know only their interface

I have a class to manipulate objects with defined interface
class TaskManager
{
/**
* #param TaskInterface $task
* #param string $command
* #return TaskInterface
*/
public static function editTask($task, $command)
{
$task->setStatus(TaskInterface::TASK_STATUS_ACTIVE);
$task->setCommand($command);
$task->taskSave();
return $task;
}
}
I can create single object by passing its instance as an method argument. This is pretty straightforward. But how should I create many of them?
public static function export()
{
$commands = self::getCommandsToAdd();
foreach($commands as $c){
//This is wrong.
$task = new TaskInterface();
$task->setCommand($c);
$task->save();
//don't need to return it if it's saved
}
}
I can't create it this way. And it's obviously a bad idea to pass array of new objects. Another way is to pass a class name as a string and call its method to retrieve new object. But it seems wrong as well
I think I figured out a solution by myself. Can use a factory interface to pass a factory object.
interface TaskFactoryInterface
{
public static function createNew();
}
/**
* #param TaskFactoryInterface $task_factory
*/
public static function export($task_factory)
{
$commands = self::getCommandsToAdd();
foreach($commands as $c){
$task = $task_factory::createNew();
$task->setCommand($c);
$task->save();
//don't need to return it if it's saved
}
}
What do you think?

PHPUnit issue asserting mock is a ReflectionProperty object using Laravel

I am new to PHPUnit and am trying to set up my first test. It is failing with the following error message
Failed asserting that ReflectionProperty Object (...) is an instance of class "Illuminate\Database\Eloquent\Model".
Essentially, I am trying to mock the creation of a model which I am injecting into a constructor and testing it is the right type through reflection. I am following the advice laid out in this post/answer:
How do I test this class using phpunit?
If anyone can give me some guidance, that would greatly appreciated.
The class being tested is here:
<?php
namespace PlaneSaleing\Repo\Listing;
use Illuminate\Database\Eloquent\Model;
class EloquentListing implements ListingInterface {
protected $advert;
public function __construct(Model $advert)
{
$this->advert = $advert;
}
/**
* Get paginated listings
*
* #param int Current page
* #param int Number of listings per page
* #return StdClass object with $items and $totalItems for pagination
*/
public function byPage($page=1, $limit=10)
{
$result = new \StdClass;
$result->page = $page;
$result->limit = $limit;
$result->totalItems = 0;
$result->items = array();
$listings = $this->advert
->orderBy('created_at')
->skip( $limit * ($page-1) )
->take($limit)
->get();
// Create object to return data useful for pagination
$result->items = $listings->all();
$result->totalItems = $this->totalListings();
return $result;
}
The service provider is here:
<?php
namespace PlaneSaleing\Repo;
use Illuminate\Support\ServiceProvider;
use PlaneSaleing\Repo\Listing\EloquentListing as Listing;
use \Advert;
class RepoServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('PlaneSaleing\Repo\Listing\ListingInterface', function($app) {
$app->make(Listing(new Advert));
} );
}
}
My Test is here:
<?php
use \Mockery;
use \ReflectionClass;
use PlaneSaleing\Repo\Listing\EloquentListing;
class EloquentListingTest extends \TestCase
{
/**
* Testing if __constructor is setting up property
*/
public function testModelSetsUp()
{
$mock1 = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($mock1);
$reflection = new ReflectionClass($listing);
// Making your attribute accessible
$property1 = $reflection->getProperty('advert');
$property1->setAccessible(true);
$this->assertInstanceOf(Illuminate\Database\Eloquent\Model::class, $property1);
}
When you assert, you should use the actual value of the reflection property, not the property itself:
public function testModelSetsUp()
{
$mock1 = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($mock1);
$reflection = new ReflectionClass($listing);
// Making your attribute accessible
$property1 = $reflection->getProperty('advert');
$property1->setAccessible(true);
$this->assertInstanceOf(
Illuminate\Database\Eloquent\Model::class,
$property1->getValue($listing)
);
}
However, you can simplify the test a lot:
public function testModelSetsUp()
{
$mock1 = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($mock1);
$this->assertAttributeInstanceOf(
Illuminate\Database\Eloquent\Model::class,
'advert',
$listing
);
}
Even better, you could use meaningful variable names, use a meaningful test method name, and not just assert that the $advert property is an instance of Illuminate\Database\Eloquent\Model, but actually the same instance as passed into the constructor:
public function testConstructorSetsAdvert()
{
$advert = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($advert);
$this->assertAttributeSame(
$advert,
'advert',
$listing
);
}

PHP singelton class for PDO connection

I have designed following class
<?php
class DB {
private static $objInstance;
/*
* Class Constructor - Create a new database connection if one doesn't exist
* Set to private so no-one can create a new instance via ' = new DB();'
*/
private function __construct() {}
/*
* Like the constructor, we make __clone private so nobody can clone the instance
*/
private function __clone() {}
/*
* Returns DB instance or create initial connection
* #param
* #return $objInstance;
*/
public static function getInstance( ) {
if(!self::$objInstance){
$ini_array = parse_ini_file("db.ini");
$dbInfo['server'] = $ini_array['server'];
$dbInfo['database'] = $ini_array['database'];
$dbInfo['username'] = $ini_array['username'];
$dbInfo['password'] = $ini_array['password'];
$dsn = 'mysql:host='.$dbInfo['server'].';dbname='.$dbInfo['database'].'';
self::$objInstance = new PDO(DB_DSN, $dbInfo['username'], $dbInfo['password']);
self::$objInstance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$objInstance;
} # end method
/*
* Passes on any static calls to this class onto the singleton PDO instance
* #param $chrMethod, $arrArguments
* #return $mix
*/
final public static function __callStatic( $chrMethod, $arrArguments ) {
$objInstance = self::getInstance();
return call_user_func_array(array($objInstance, $chrMethod), $arrArguments);
} # end method
}
?>
My problem is when I want to perform a query I get following error:
Fatal error: Call to undefined method DB::query()
foreach(DB::query("SELECT * FROM content_type_video") as $row){
print_r($row);
}
Any ideas why and how to fix this?
$db = DB::getInstance();
foreach($db->query("SELECT * FROM content_type_video") as $row){
print_r($row);
}
The functionality you want to make use of is not available in your PHP version - you need to upgrade to at least PHP 5.3 for __callStatic.
However I suggest you to stop using singletons anyway and stop using static function calls everywhere in your code.

Object collection classes or not

I'm trying to decide whether to create many classes for each content type I have in my application/database or just stick with procedural code.
Version 1:
make a class for each object collection:
class App{
protected $user_collection;
function getUserCollection(){
if(!isset($this->user_collection)
$this->user_collection = new UserCollection($this);
return $this->user_collection;
}
// ...
}
class UserCollection{
function __construct(App $app){
$this->app = $app;
}
function getUser($user){
return new User($this->app, $user);
}
function getUsers($options){
$users = $this->app->getDatabase()->query($options);
foreach($users as &$user)
$user = new User($this, $user);
return $users;
}
// ...
}
which I'm using like:
$app = new App();
echo $app->getUserCollection()->getUser('admin')->email_address;
version 2:
keep all methods in a single class
class App{
function getUsers($options){
$users = $this->getDatabase()->query($options);
foreach($users as &$user)
$user = new User($this, $user);
return $users;
}
function getUser($user){
return new User($this, $user);
}
// ...
}
used like:
$app = new App();
echo $app->getUser('admin')->email_address;
version 3:
make getUsers() a a static method in the "User" class (the method instantiates a new User object):
$app = new App();
echo User::getUser($app, 'admin')->email_address;
Which way should I go? The "user" object is just an example, App has other objects too, like "database", "pages" etc.
I would use your version 1, but I would make getUser() and getUsers() methods of App.
This gets rid of the awkward getUserCollection() call, because instead inside the getUser() and what not you just call $this->user_collection.
Personnaly, I often used the second one with method like this:
class user {
/**
* Load object from ...
*/
public function load($userId) {}
/**
* Insert or Update the current object
*/
public function save() {}
/**
* Delete the current object
*/
public function delete() {
// delete object
// Reset ID for a future save
$this->UserID = null;
}
/**
* Get a list of object
*/
public static function getList() {
// Make your search here (from DB)
// Put rows into new "SELF" object
$list = array();
foreach($rows as $row) {
$obj = new self();
$obj->populate($row);
$list[$obj->UserID] = $obj; // Associative array or not...
}
}
}
Like you can see, I set my "getList" function static to simply access like this:
$listUsers = user::getList();
OK, it's very simple but work in most case of simple app.

Categories