i have hundred of class model (Model of MVC system).
How i can create instance with relational class?
example in class there is method like this:
class object {
/**
* Simply create new instance of this object
* #return object
*/
function createNewInstance() {
$class_name = __CLASS__;
$return = new $class_name;
return $return;
}
}
As we can see, i use __CLASS__ to get relational name if this class.
Is there any better way to create instance?
i heard there is reflection method to do it?
seems you need get_class()
http://codepad.org/yu6R1PDA
<?php
class MyParent {
/**
* Simply create new instance of this object
* #return object
*/
function createNewInstance() {
//__CLASS__ here is MyParent!
$class_name = get_class($this);
return new $class_name();
}
}
class MyChild extends MyParent {
function Hello() {
return "Hello";
}
}
$c=new MyChild();
$d=$c->createNewInstance();
echo $d->Hello();
this also works:
class MyParent {
/**
* Initialise object, set random number to be sure that new object is new
*/
function __construct() {
$this->rand=rand();
}
}
class MyChild extends MyParent {
function Hello() {
return "Hello ".$this->rand;
}
}
$c=new MyChild();
$d=new $c;
echo $c->Hello()."\n";
echo $d->Hello()."\n";
Related
I have a abstract class Foo and a abstract builder FooBuilder
abstract class Foo {
}
abstract class FooBuilder {
protected Foo $model;
/**
* Return the class instance
*
* #return Model //What is the correct return type??
*/
public function get()
{
return $this->model;
}
}
I want to use the method get() in my child Builders and the IDE detect that the return type is the child class, not the abstract Foo.
class Bar extends Foo {
}
abstract class BarBuilder {
public function __construct()
{
$this->model = new Bar();
}
}
$barBuilder = BarBuilder();
$bar = $barBuilder->get(); //The type is "Bar", but IDE thinks is "Foo"
Is there any way of returning a static type of a attribute not the class in PHPDoc? Something like #return static($this->model)?
An example is what Laravel's Eloquent does in SomeModel::find(). The IDE knows that the type can be SomeModel. But the #return only have Model.
You should probably use Foo as the return type in your example; but for fun, you can use static return type to determine child instance like below
class Foo
{
/**
* #return string
*/
public function Iam(): string
{
return "hello";
}
}
class Helper
{
/**
* Return the class instance
*
* #return static
*/
public static function get(): static
{
return new self();
}
}
class FooBuilder extends helper
{
protected Foo $model;
public function mememe()
{
echo "I am a method";
}
}
class FooBuilder2 extends helper
{
protected Foo $model;
public function xray()
{
echo "I am a method";
}
}
$test = FooBuilder::get();
$test->mememe();
$test2 = FooBuilder2::get();
$test2->xray();
Using generics in docblock does help PhpStorm(mine is 2021.2.2):
class Foo
{
}
/**
* #template T of Foo
*/
class FooBuilder
{
/**
* #var T
*/
protected Foo $model;
/**
* #return T
*/
public function get(): Foo
{
return $this->model;
}
}
class Child extends Foo
{
public function testCompletion(){}
}
/**
* #extends FooBuilder<Child>
*/
class ChildBuilder extends FooBuilder
{
}
class AnotherChild extends Foo
{
public function otherMethod(){}
}
/**
* #extends FooBuilder<AnotherChild>
*/
class AnotherChildBuilder extends FooBuilder
{
}
$childBuilder = new ChildBuilder();
$childBuilder->get()->testCompletion(); // testCompletion is suggested
$anotherChildBuilder = new AnotherChildBuilder();
$anotherChildBuilder->get()->otherMethod(); // otherMethod is suggested
base classes:
abstract class A
{
public function methodA() { echo __FUNCTION__.'<br>'; }
}
class B extends A
{
public function methodB() { echo __FUNCTION__.'<br>'; }
}
so far so good.
A factory which can create any of them:
class Factory
{
/**
* #return A
*/
public static function createAorB()
{
return new B();
}
}
phpdoc is right, since it say "return something comes from A".
Now the tricky part comes:
class OwnClass
{
/**
* #var A
*/
protected $var;
public function __construct()
{
$this->var = \Factory::createAorB();
}
}
class OwnClassB extends OwnClass
{
public function callMe()
{
$this->var->methodA();
$this->var->methodB(); // this will also run, but IDE wont autocomplete with this method
}
}
(new OwnClassB())->callMe();
this will run for sure, but check for the methodB() calling. IDE wont autocomplete with this method. How to tell that var is now B, not A with phpDoc?
You can override inherited properties for PHPStorm's autocompletion:
/**
* #property B $var
*/
class OwnClassB extends OwnClass
{
//...
}
However, I personally think you're trying to solve the wrong problem. If you have a Factory class that returns either A or B, you shouldn't type hint the return value to A. Instead, you should provide both:
class Factory
{
/**
* #return A|B
*/
public static function createAorB()
{
return new B();
}
}
Both options inform PHPStorm enough to let it show you both methods as autocomplete options, but the latter is accurate in terms of what the code actually does (well, not in this example, but given the function name I guess that's what your real-world implementation does).
I have created my own DB - Model structure which is similar to Laravel. I have been facing with 2 problems.
I have a Model class which all of my models extend it. For example, my User class extends Model. I want to return that get() method return type of class which is extended.
Is this possible?
Class Model extends DB {
/**
* #return AnyClassThatExtended
*/
function get()
{
}
}
Class User extends Model {
function test() {
$user->get(); // I want it to return User type of object
}
}
You should use
private static $instance;
/**
* return static
*/
public function get() {
if (is_null(self::$instance)) {
self::$instance = new static();
}
return self::$instance;
}
because you are returning current class that you are at (if I understand correctly)
It's possible that PHPStorm does not recognize it
I'd like to create an object like so:
class Obj extends BaseModel {
public function doSomething() {
// do some stuff with the instance
}
}
class BaseModel {
public static function find($id){
// retrieve object from database and return instance of Obj class
}
}
So i can achieve the following:
$obj1 = Obj::find(1);
$obj1->doSomething();
How can I create this so that the static method from the base class returns an instance of the Obj class?
(similar to how Laravel handles objects)
In your base model, you can get the class of the children that's been called with get_called_class.
class Obj extends Base{
private $id;
public function __construct($id){
$this->id = $id;
}
public function getId(){
return $id;
}
}
class Base{
public static function find($id){
$class = get_called_class();
return new $class($id);
}
}
Example
$obj = Obj::find(1);
var_dump of $obj
object(Obj)#1 (1) {
["id":"Obj":private]=>
int(1)
}
I want to know if there is a solution on how to unit-test a PHP trait.
I know we can test a class which is using the trait, but I was wondering if there are better approaches.
Thanks for any advice in advance :)
EDIT
One alternative is to use the Trait in the test class itself as I'm going to demonstrate bellow.
But I'm not that keen on this approach since there is no guaranty there are no similar method names between the trait, the class and also the PHPUnit_Framework_TestCase (in this example):
Here is an example trait:
trait IndexableTrait
{
/** #var int */
private $index;
/**
* #param $index
* #return $this
* #throw \InvalidArgumentException
*/
public function setIndex($index)
{
if (false === filter_var($index, FILTER_VALIDATE_INT)) {
throw new \InvalidArgumentException('$index must be integer.');
}
$this->index = $index;
return $this;
}
/**
* #return int|null
*/
public function getIndex()
{
return $this->index;
}
}
and its test:
class TheAboveTraitTest extends \PHPUnit_Framework_TestCase
{
use TheAboveTrait;
public function test_indexSetterAndGetter()
{
$this->setIndex(123);
$this->assertEquals(123, $this->getIndex());
}
public function test_indexIntValidation()
{
$this->setExpectedException(\Exception::class, '$index must be integer.');
$this->setIndex('bad index');
}
}
You can test a Trait using a similar to testing an Abstract Class' concrete methods.
PHPUnit has a method getMockForTrait which will return an object that uses the trait. Then you can test the traits functions.
Here is the example from the documentation:
<?php
trait AbstractTrait
{
public function concreteMethod()
{
return $this->abstractMethod();
}
public abstract function abstractMethod();
}
class TraitClassTest extends PHPUnit_Framework_TestCase
{
public function testConcreteMethod()
{
$mock = $this->getMockForTrait('AbstractTrait');
$mock->expects($this->any())
->method('abstractMethod')
->will($this->returnValue(TRUE));
$this->assertTrue($mock->concreteMethod());
}
}
?>
You can also use getObjectForTrait , then assert the actual result if you want.
class YourTraitTest extends TestCase
{
public function testGetQueueConfigFactoryWillCreateConfig()
{
$obj = $this->getObjectForTrait(YourTrait::class);
$config = $obj->getQueueConfigFactory();
$this->assertInstanceOf(QueueConfigFactory::class, $config);
}
public function testGetQueueServiceWithoutInstanceWillCreateConfig()
{
$obj = $this->getObjectForTrait(YourTrait::class);
$service = $obj->getQueueService();
$this->assertInstanceOf(QueueService::class, $service);
}
}
Since PHP 7 we can now use annonymous classes...
$class = new class {
use TheTraitToTest;
};
// We now have everything available to test using $class