yiisoft / yii2

Yii 2: The Fast, Secure and Professional PHP Framework
http://www.yiiframework.com
BSD 3-Clause "New" or "Revised" License
14.24k stars 6.91k forks source link

Add static method to return the class name in Object class #172

Closed petrabarus closed 11 years ago

petrabarus commented 11 years ago

One of great hassle when I develop using Yii1.1 is that I have to write the name of the class manually for methods like beginWidget etc. And it's hard to use it using IDE like Netbeans.

I'm proposing to add get_called_class wrapper in Object class http://php.net/manual/en/function.get-called-class.php

Since the minimum PHP version is 5.3.0, I think this is okay.

It will be something like this

   ///Object.php
    /**
     * Returns the class name.
     * @return string the name of the class.
     */
    public static function getClassName()
    {
        return get_called_class();
    }

that way I can easily use this in the view, or somewhere else.

/* @var $this yii\base\View */
$this->widget(app\components\TestWidget::getClassName());

and the IDE can easily recognize the TestWidget class, and I can easily click on it to go to the file.

qiangxue commented 11 years ago

I'm not the judger of language. :) It just doesn't feel right to me (not aware any other language would allow this usage).

creocoder commented 11 years ago

It just doesn't feel right to me

If you doesn't feel then Yii 2 will use "feel right" variant. Right? ;) I just want be consistent with Yii 2 code.

andersonamuller commented 11 years ago

I didn't check the code deeply, but with this new additional, can't you do like below now?

$modelClass = $this->owner->className();
$query = $modelClass::find();
$db = $modelClass::getDb();
creocoder commented 11 years ago

@andersonamuller You use $this->owner->className() So you call static non-static way already ) And if so why not call all it non-static way? Its shorter )

$query = $this->owner->find();
$db = $this->owner->getDb();
andersonamuller commented 11 years ago

@creocoder Sure, I got that, just thinking about the possibilities ;)

creocoder commented 11 years ago

@andersonamuller Another way is:

$owner = $this->owner;
$modelClass = $owner::className();
$query = $modelClass::find();
$db = $modelClass::getDb();

We have no non-static way calls here. But dont you think its really overcoding in compare with:

$query = $this->owner->find();
$db = $this->owner->getDb();

?

andersonamuller commented 11 years ago

I think you mean:

$modelClass = get_class($this->owner);
$query = $modelClass::find();
$db = $modelClass::getDb();

This way we don't have static method called non-static way.

creocoder commented 11 years ago

@andersonamuller Yes, but if non-static way calls legal why not? (since code is shorter and work correct)

rinatio commented 11 years ago

Although I'm late to the party, Why not pass new object into $this->widget, which allows to use type hinting? This way for example:

$this->widget(new app\components\TestWidget($options));
suralc commented 11 years ago

@rinatio It's nice, but should be optional. Optional ViewRenderers might be able to call functions and methods (Widget::className() // $this->widget()), but some do not support object creation in views. We'd need to create the object the controller then, wich would be unideal.

rinatio commented 11 years ago

@suralc I'm wondering why would ViewRenderer not support object creation, but support method calls? What ViewRenderer it is?

cebe commented 11 years ago

What ViewRenderer it is?

Smarty for example.

qiangxue commented 11 years ago

I don't think we should support $this->widget(new ClassName)) as it brings confusion.

I'm closing this issue as the original issue has been resolved: the className() has its value in several places, as shown in https://wiki.php.net/rfc/class_name_scalars

klimov-paul commented 11 years ago

Why we can not use widget signature like following:

<?php $form = ActiveForm::begin(); ?>
...
<?php Captcha::run(); ?>
...
<?php ActiveForm::end(); ?>

Using static method for the Widget class will allow maintaining of widget stack, while class name will be used explicitely. Also "Widget::begin()" and "Widget::end()" may serve just like code brackets.

qiangxue commented 11 years ago

Widget now has a view property which contains reference to the view object in which the widget is created.

qiangxue commented 11 years ago

Perhaps this?

$form = ActiveForm::begin($this, array(...));

echo new Captcha($this, array(...));

ActiveForm::end();
klimov-paul commented 11 years ago

I just thought about the same thing

klimov-paul commented 11 years ago

Although, I think running widget on construction is not a good idea: echo new Captcha($this, array(...)); For me the following is better: Captcha::run($this, array(...));

qiangxue commented 11 years ago

It's not running in constructor. It's done via the __toString() magic function which calls run(). You can explicitly do this:

$captcha = new Captcha($this);
$captcha->xyz = 'xyz';
echo $captcha->run();
klimov-paul commented 11 years ago

It's not running in constructor. It's done via the __toString() magic function

Alright, It is fine then.

qiangxue commented 11 years ago

@yiisoft/core-developers What's your take on this new proposal? It's a dramatic usage change. I'd like to hear other opinions first.

samdark commented 11 years ago

pros

cons

qiangxue commented 11 years ago

Yeah, the exception throwing is a big drawback.

samdark commented 11 years ago

So I think we can go with

<?php Captcha::widget($this, array(...)); ?>

It will create an instance, configure it and run it w/o using __toString. Same pros w/o one con.

qiangxue commented 11 years ago

sounds good to me, similar to what @klimov-paul proposed, but I like "::widget()" better as it gives a sense of object creation.

I suggest "widget()" should return a string so that it is consistent with render().

So <?php echo Captcha::widget($this); ?>.

klimov-paul commented 11 years ago

Spelling: yii\widgets\Captcha::widget(); sounds like tautology. Also i would like to keep methods "Widget::begin()", "Widget::end()", which inconsisent with "Widget::widget()". In case of using "Widget::widget()" these should be "Widget::beginWidget()", "Widget::endWidget()".

Although for me naming is not the main thing here.

qiangxue commented 11 years ago

If you use use statement, you would only see Captcha::widget().

It looks ok to me having Widget::begin(), Widget::end() and Widget::widget() together.

andersonamuller commented 11 years ago

What's wrong with having:

/* @var $this yii\base\View */
$widget = Widget::create($this); // will behave as Widget::begin()
$widget->foo = 'Something';
$widget->run(); // will behave as Widget::end()

and you can also do

/* @var $this yii\base\View */
Widget::create($this)->run();
rinatio commented 11 years ago

With php5.4 you could:

echo (new Captcha($this, $options))->run();

For php5.3 (or smarty) you can do echo Captcha::create($this, $options))->run();

public function create()
{
    $r = new ReflectionClass(get_called_class());
    return $r->newInstanceArgs(func_get_args());
}
qiangxue commented 11 years ago

Implemented begin(), end() and widget().

You can also use the alternative syntax like following. The main difference is that begin() and end() look better as they serve as the boundary of a block. And they also check if the calls are properly nested. widget() will return the rendering result.

$widget = new Widget($this);
$widget->run();
petrabarus commented 11 years ago

Just curious, Any possibility that this enhancement could make into Yii1 as well?

cebe commented 11 years ago

I don't think we will change the way it works in yii 1.1

petrabarus commented 11 years ago

Yes no need to add it in Yii1.1.

The PHP 5.5 has just been released, and there is that name scalar there as promised. http://www.php.net/ChangeLog-5.php#5.5.0

I guess we should just use the PHP 5.5 instead of adding this method.

samdark commented 11 years ago

Yes, if you're using 5.5 you can do it.