Create Class object from variable value in Laravel - php

I want to create an object of a class from a returned string but I am getting error Class **test_report** not found. My code:
public function display_report_builder($report_name = null)
{
$column_listing = new $report_name;// gets the test_report
return view('column_list')->with(['column_list_names' => $column_listing->columns]);
}

This isn't the better approach here. What you should do is to use a Factory design pattern:
class ReportFactory
{
public static function create($report_name)
{
switch($report_name) {
case 'test_report': return new TestReport();
default: throw new Exception('report not found');
}
}
}
Then you call with $column_listing = ReportFactory::create($report_name);
Why? Because you avoid "magic variables" with unknown data; you can trace errors properly; you can use namespace; you can extend functionalities easily, and easily activate or deactivate objects (or reports in this case); you have a cleaner code, and so on...

test if the class name (string) really is a valid class :
public function display_report_builder($report_name = null)
{
$column_list_names = null;
if (class_exists($report_name) && is_a($report_name, App\reports\test_report::class, true)) {
$column_listing = new $report_name;
$column_list_names = $column_listing->columns;
}
return view('column_list', compact('column_list_names'));
}
is_a() : Checks if the given object is of this class or has this class
as one of its parents.

Related

Replace all class instances with stub

I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.

redundant getters and setters in lazy-loading and dependency-injection pattern

I'm implementing the lazy initialization and dependency injection pattern in my PHP application at the moment and face the following question:
Every class has a bunch of getter and setter methods providing objects of foreign classes. Is there an elegant way to remove this "redundancy" to a parent class or some kind of factory?
EDIT: without loosing the advantages of testability ;-)
EDIT2: here is an example of a getter and setter method like I use them:
function getSql() {
if (is_object($this->sql) === FALSE) {
$registry = \Registry::getInstance();
$factories = $registry->get('factories');
$database = $factories['databaseSql'];
$this->sql = $database->getSql();
}
return $this->sql;
}
function setSql($sqlObject) {
// ... some checks
$this->sql = $sqlObject;
}
EDIT3 I followed the idea of using traits so here is a sample solution using a trait for the class "Registration":
trait Registration {
private $registration = null;
public function getRegistration() {
$registry = \Registry::getInstance();
$factories = $registry->get('factories');
if (is_object($this->registration) === TRUE) {
// get obect if instance already exists
$registration = $this->registration;
} else if (isset($factories['registration']) === TRUE) {
// get object using the matching factory
$registrationFactory = $factories['registration'];
$registration = $registrationFactory->getRegistration();
} else if (class_exists('\engine\classes\Registration') === TRUE) {
// get object using the default object class when no specific factory is defined
$registration = new \engine\classes\Registration();
} else {
throw new \Exception('error getting an instance of Registration');
}
$this->registration = $registration;
return $registration;
}
public function setRegistration($object) {
if (is_object($object) === FALSE)
throw new \Exception('invalid registration object');
$this->registration = $object;
}
}
Usage
class Foo {
use Registration;
public function bar() {
$reg = $this->getRegistration();
// ... use instance of Registration
}
}
Do you know OOP advantages of PHP language?
Inheritance
Traits
Magic getter and setters
May be some Aspect-Oriented Programming. For example Implementing Reusable Fluent Interface Pattern in PHP With AOP.
It's so many links because you did not provide any code for example.

Injecting single-use object into class

I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.

Anonymous class construction

I need an idea to create anonymous class on PHP. I don't know how I can works.
See my limitations:
On PHP you can't make anonymous class, like anonymous function (like class {});
On PHP you don't have class scope (except in namespaces, but it have the same problem below);
On PHP you can't use variables to specify the class name (like class $name {});
I don't have access to install the runkit PECL.
What I need, and why:
Well, I need create a function called ie create_class() that receives a key name and a anonymous class. It'll be useful for me because I want use different name class symbols that PHP can't accept. For instance:
<?php
create_class('it.is.an.example', function() {
return class { ... }
});
$obj = create_object('it.is.an.example');
?>
So, I need an idea that accept this use. I need it because on my framework I have this path: /modules/site/_login/models/path/to/model.php. So, the model.php need to declare a new class called site.login/path.to.model.
On call create_object() if the internal cache have a $class definition (like it.is.an.example it simply return the new class object. If not, need load. So I will use the $class content to search fastly what is the class file.
In PHP 7.0 there will be anonymous classes. I don't fully understand your question, but your create_class() function might look like this:
function create_class(string $key, array &$repository) {
$obj = new class($key) {
private $key;
function __construct($key) {
$this->key = $key;
}
};
$repository[$key] = $obj;
return $obj;
}
This will instantiate an object with an anonymous class type and register it into the $repository. To get an object out you use the key you created it with: $repository['it.is.an.example'].
You can create a dummy class using stdClass
$the_obj = new stdClass();
So basically you want to implement a factory pattern.
Class Factory() {
static $cache = array();
public static getClass($class, Array $params = null) {
// Need to include the inc or php file in order to create the class
if (array_key_exists($class, self::$cache) {
throw new Exception("Class already exists");
}
self::$cache[$class] = $class;
return new $class($params);
}
}
public youClass1() {
public __construct(Array $params = null) {
...
}
}
Add a cache within to check for duplicates
If you really need to to that, you could use eval()
$code = "class {$className} { ... }";
eval($code);
$obj = new $className ();
But the gods won't approve this. You will go to hell if you do it.

PHP constructor to return a NULL

I have this code. Is it possible for a User object constructor to somehow fail so that $this->LoggedUser is assigned a NULL value and the object is freed after constructor returns?
$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false)
$this->LoggedUser = new User($_SESSION['verbiste_user']);
Assuming you're using PHP 5, you can throw an exception in the constructor:
class NotFoundException extends Exception {}
class User {
public function __construct($id) {
if (!$this->loadById($id)) {
throw new NotFoundException();
}
}
}
$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false) {
try {
$this->LoggedUser = new User($_SESSION['verbiste_user']);
} catch (NotFoundException $e) {}
}
For clarity, you could wrap this in a static factory method:
class User {
public static function load($id) {
try {
return new User($id);
} catch (NotFoundException $unfe) {
return null;
}
}
// class body here...
}
$this->LoggedUser = NULL;
if ($_SESSION['verbiste_user'] != false)
$this->LoggedUser = User::load($_SESSION['verbiste_user']);
As an aside, some versions of PHP 4 allowed you to set $this to NULL inside the constructor but I don't think was ever officially sanctioned and the 'feature' was eventually removed.
AFAIK this can't be done, new will always return an instance of the object.
What I usually do to work around this is:
Adding a ->valid boolean flag to the object that determines whether an object was successfully loaded or not. The constructor will then set the flag
Creating a wrapper function that executes the new command, returns the new object on success, or on failure destroys it and returns false
-
function get_car($model)
{
$car = new Car($model);
if ($car->valid === true) return $car; else return false;
}
I'd be interested to hear about alternative approaches, but I don't know any.
Consider it this way. When you use new, you get a new object. Period. What you're doing is you have a function that searches for an existing user, and returns it when found. The best thing to express this is probably a static class function such as User::findUser(). This is also extensible to when you're deriving your classes from a base class.
A factory might be useful here:
class UserFactory
{
static public function create( $id )
{
return (
filter_var(
$id,
FILTER_VALIDATE_INT,
[ 'options' => [ 'min_range' => 1, ] ]
)
? new User( $id )
: null
);
}
}
When a constructor fails for some unknown reason, it won't return a NULL value or FALSE but it throws an exception. As with everything with PHP5. If you don't handle the exception then the script will stop executing with an Uncaught Exception error.
maybe something like this:
class CantCreateException extends Exception{
}
class SomeClass {
public function __construct() {
if (something_bad_happens) {
throw ( new CantCreateException());
}
}
}
try{
$obj = new SomeClass();
}
catch(CantCreateException $e){
$obj = null;
}
if($obj===null) echo "couldn't create object";
//jaz303 stole my idea an wrap it into a static method

Categories