[WIP] Significant reduction in memory consumption #1426

Closed
wants to merge 13 commits into from

10 participants

@creocoder

With this fix:

    [CComponent:_e] => array
    (
        'onbeforesave' => CList#8
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#9
                    (
                        [createAttribute] => 'create_time'
                        [updateAttribute] => 'update_time'
                        [setUpdateOnCreate] => false
                        [timestampExpression] => null
                        [CBehavior:_enabled] => true
                        [CBehavior:_owner] => Test#1(...)
                        [CComponent:_e] => null
                        [CComponent:_m] => null
                    )
                    1 => 'beforeSave'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
    )

Without:

    [CComponent:_e] => array
    (
        'onafterconstruct' => CList#16
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17
                    (
                        [createAttribute] => 'create_time'
                        [updateAttribute] => 'update_time'
                        [setUpdateOnCreate] => false
                        [timestampExpression] => null
                        [CBehavior:_enabled] => true
                        [CBehavior:_owner] => Test#1(...)
                        [CComponent:_e] => null
                        [CComponent:_m] => null
                    )
                    1 => 'afterConstruct'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
        'onbeforevalidate' => CList#18
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17(...)
                    1 => 'beforeValidate'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
        'onaftervalidate' => CList#19
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17(...)
                    1 => 'afterValidate'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
        'onbeforesave' => CList#20
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17(...)
                    1 => 'beforeSave'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
        'onaftersave' => CList#21
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17(...)
                    1 => 'afterSave'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
        'onbeforedelete' => CList#22
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17(...)
                    1 => 'beforeDelete'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
        'onafterdelete' => CList#23
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17(...)
                    1 => 'afterDelete'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
        'onbeforefind' => CList#24
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17(...)
                    1 => 'beforeFind'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
        'onafterfind' => CList#25
        (
            [CList:_d] => array
            (
                0 => array
                (
                    0 => CTimestampBehavior#17(...)
                    1 => 'afterFind'
                )
            )
            [CList:_c] => 1
            [CList:_r] => false
            [CComponent:_e] => null
            [CComponent:_m] => null
        )
    )
@creocoder

@qiangxue What do you think about this catch? :)

@qiangxue
Yii Software LLC member

It's a good catch, but the fix has the drawback that it makes the methods implicit and thus the corresponding API doc is gone.

Instead of using this fix, one can work around the issue by overriding the events() method so that only the needed event handlers are attached.

@creocoder

@qiangxue Yes you right. But behavior developer need to think always about events() method. May be we can support him on core level? With API docs I think we can do something...

@creocoder

@qiangxue Another approach here. Without drawbacks (i think).

@creocoder

Seems my solution have side effects with behaviors child classes. I think we need add note about override event() in doc for behaviors delevopers and fix framework behaviors.

@samdark
Yii Software LLC member

BC break? I don't think we want it. Still reducing memory consumption is a very good thing.

@slavcodev
public function attach($owner)
{
    $this->_owner=$owner;
    if(($events=$this->events())!==array()){
        $reflector=new ReflectionClass($this);
        foreach($events as $event=>$handler){
            $methodClass=$reflector->getMethod($handler)->class;
            if($methodClass!=='CModelBehavior'&&$methodClass!=='CActiveRecordBehavior')
                $owner->attachEventHandler($event,array($this,$handler));
        }
    }
}

BC don't break and work with children classes.

@samdark
Yii Software LLC member

@slavcodev It will not work for custom components extended from CComponent or CApplicationComponent. I don't think it's a good idea to hadcode class names.

@samdark
Yii Software LLC member

I think we can go with blank methods removal. It's BC. Problem with IDEs completion can be solved via some additional PHPDoc. Documentation from methods can be moved to class header.

@qiangxue
Yii Software LLC member

I think what @slavcodev suggested is a good compromise. The memory issue is important mainly when there are many instances of the same component class. This mainly happens to ActiveRecord.

I'm against removing the methods. Although we can put the needed doc into the class header, it's not neat. More importantly, we don't have class header inheritance (i.e. methods defined in the parent class won't have their doc showing in the child class header, if we choose to remove the methods.)

@samdark
Yii Software LLC member

@qiangxue so you think fixing it for two specific classes is enough?

@creocoder

I don't think it's a good idea to hadcode class names too. I vote for method_exists() + move doc to header.

Why:

  • faster than without fix
  • solve memory problems
@qiangxue
Yii Software LLC member

@samdark It's enough for most cases. This problem mainly happens to base classes. It's not common that people will develop new base behavior classes. If they do, they can use the class_exists() approach to avoid the memory issue.

@creocoder

I insist on the fact that the fix should be the same as in the last commit.

Without fix
Time: 1.50965 s
Memory: 12,467KB

With fix
Time: 0.55263 s
Memory: 5,681KB

Yii::beginProfile('test');
$array=array();
for($i=0;$i<10000;$i++)
    $array[]=new Test;
Yii::endProfile('test');
@qiangxue
Yii Software LLC member

What about the performance result of what @slavcodev suggested? And how did you do the test?

@creocoder

@qiangxue:

@slavcodev:

Time: 0.64722 s
Memory: 5,693KB

@qiangxue
Yii Software LLC member

Then I vote for @slavcodev's approach.
For the removing method approach, consider this scenario: a developer wants to create a AR behavior and do something about beforeValidate(). He checks the API doc for CActiveRecordBehavior, but there's no a single place mentioning about beforeValidate() if we remove the declaration of beforeValidate(). He will only be able to find the info about beforeValidate() when he checks the parent class doc.

@creocoder

@qiangxue I have more interesting idea. Will show shortly.

@creocoder

@qiangxue I think we have ideal solution in last commit:

Time: 0.80 s
Memory: 5,722KB

There is no hardcoded classes.

@qiangxue
Yii Software LLC member

Yes, this is better. Could you please double check to make sure it works with PHP 5.1?

@qiangxue
Yii Software LLC member

One potential problem is that someone defines an abstract behavior class with non-empty methods which contain default implementation.

@creocoder

@qiangxue Can we ignore this potential problem? Or we need other solution?

@Ragazzo

@creocoder am, i got this one problem that @qiangxue discribed. I mean i have component which has defaults 3 behaviors, that has some defaults method, so i will get en error here?am i right?

@creocoder

@Ragazzo This behaviors is abstract?

@Ragazzo

@creocoder in on use-case yes(other behaviors in modules just overrides him - this one is abstract), and some other not, i've described them above, they just extending CBehavior. I think that your solution for memory consumption is good, can u then just describe how to fix this problem, if your solution will be accepted.

@samdark
Yii Software LLC member

There's no solution possible based on class-level detection. Custom class can both declare new events and implement existing ones. Additionally we can't easily find out if method body is empty or not even using Reflection.

I see two possible solutions:

  1. Remove methods, move docs to class-level phpdoc. Possibly update docs parser (it worth the effort because of performance increase and memory usage decrease).
  2. Add phpdoc tags to each event declaration, read it with reflection and exclude such marked methods. I think this will require updating yiilite.php generator because we need to keep phpdoc tags.
@creocoder

I vote for 1). The quickest and easiest option. Documenting problems should not affect the performance of the framework! Solution in 1st my commit. Full BC with immediate results in the form of faster applications and use less memory.

@qiangxue
Yii Software LLC member

I still vote for @slavcodev's approach as it already solves perhaps more than 90% of the performance issue without the documentation problem, and it has comparable performance as the removing-method approach.

For the removing-method approach, besides the difficulty in documentation, another problem is that child classes don't know whether or not they can call parent::method() because it's likely the method is not implemented at all in the parent class.

Note that this performance issue is all caused by the events() design. However, the events() design is not required by IBehavior. So behavior developers can bypass this problem if they really need to.

@creocoder

@qiangxue Maybe then do not fix something and just add doc about override events() in behaviors and fix CTimestampBehavior?

@qiangxue
Yii Software LLC member

@creocoder That's an option too. If we choose to do this, we should probably also mention about this in the definitive guide (in performance section).

@creocoder

@qiangxue I hope Yii 2 have better events design without such problems.

@qiangxue
Yii Software LLC member

Well, there's no simple solution to this problem, as you have already found out.
For Yii 2, behaviors are attached to owners on demand. So if the owner doesn't use behavior, it will not have this performance issue.

@samdark
Yii Software LLC member

So currently what we need to do is:

  1. Add docs describing it in both CBehavior and performance section of the guide.
  2. Fix CTimestampBehavior.
@qiangxue
Yii Software LLC member

I would say:

  1. Apply @slavcodev's change. This will improve the performance and memory consumption of all existing behavior extensions that are derived from CModelBehavior and CActiveRecordBehavior.
  2. Add docs.
@creocoder

@qiangxue I want to try yet one approach, if it fail, then will apply @slavcodev's change.

@creocoder

@qiangxue I found faster than @slavcodev's solution without need to hardcode classes and seems without any problems.

Time: 0.77083 s
Memory: 5,722KB

See last commit.

@qiangxue
Yii Software LLC member

Unfortunately, this won't work with yiilite.php.

I know @slavcodev's solution is not perfect, but it does solve the main problem without any side effect, even though it is not 100%.

Of course, if you could come up with better solution, I would always love to see, because this problem may also exist in Yii 2.

@creocoder

@qiangxue The search is over! I did it! :)

@creocoder
Time: 0.83 s
Memory: 5,787KB
@Ragazzo

@creocoder don't want to lose anyone :D hah)

@creocoder
Time: 0.82 s
Memory: 5,755KB

Ready to merge.

@creocoder

@qiangxue For Yii 2 i found not BC solution:

/**
 * @param CEvent $event event parameter
 * @return boolean whether this handler should be attached
 */
public function someHandler($event)
{
    return false;
}

So in parent class where we can return false; to not attach empty handler. In child classes we should return true; to attach handler with implementation. This approach do not need any Reflection to work. And also good for debug needs.

@qiangxue
Yii Software LLC member

@creocoder clever approach. But...(I hate to say this), this is the same as the "abstract" approach essentially and is actually more twisted.

For Yii 2, we don't need to consider BC. Your new approach won't work because it requires the handler to be executed before it is attached.

@qiangxue
Yii Software LLC member

Perhaps a solution is to only attach public methods and skip protected methods.

@creocoder

@qiangxue I always knew that you are a genius

Time: 0.68407 s
Memory: 5,722KB
@qiangxue
Yii Software LLC member

Thank you. :) It was inspired by your creative thoughts.

Still, this approach may bring BC-breaking troubles to existing code if they happen to declare their methods to be "protected" (assuming they're writing new events).

Also, if we choose this approach, we need clear documentation.

Let's see if more drawbacks may be found by other people, or if we can have an even better solution.

@qiangxue
Yii Software LLC member

Actually, we won't have BC-breaking problem because protected methods cannot be invoked in the owner class.

@creocoder

Yes i agree with you. But this is good approach for Yii 2 i think.

@creocoder

Let this PR will still open, maybe people will have other ideas and to release (1.1.13) we will find a fully satisfactory alternative.

@slavcodev

Guys, be careful, this PR
creocoder@d020f35
may break BC, because in children behaviors may be call parent events such parent::beforeSave()

@grikdotnet

The problem with redefining a protected method can be solved by checking if the method was overridden rather then if it is protected.
I use this approach in my gCurl package to check if the callback should be executed. The response handler class has to extend an abstract class, if it overrides a callback method - it is called, if not - the callback is skipped.
Reflection:
https://github.com/grikdotnet/gCurl/blob/master/overrideReflectionClass.class.php

Though, it's not that CPU-efficient as just checking the protected status.

@samdark
Yii Software LLC member

I think we can merge protected variant. It's BC and is so far the fastest BC variant.

@samdark samdark commented on an outdated diff Sep 20, 2012
framework/base/CBehavior.php
@@ -38,15 +38,19 @@ public function events()
/**
* Attaches the behavior object to the component.
* The default implementation will set the {@link owner} property
- * and attach event handlers as declared in {@link events}.
+ * and attach public visibility event handlers as declared in {@link events}.
@samdark
Yii Software LLC member
samdark added a note Sep 20, 2012

and attach event handlers as declared in {@link events}.
Make sure you've declared handler as public and call the parent implementation if you override this method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@klimov-paul
Yii Software LLC member

@creocoder, slow down. This pull request reminds me the #819, where another eager developer attempts to reduce memory usage using inconsistent approach.

It is no doubt the extra event handlers, declared inside the behavior, will consume extra memory. BUT we can NOT prevent it using any logic operators in "CBehavior::attach()". The only way to avoid extra empty event handlers is not declaring them in "CBehavior::events()".

I suppose we should just update "CTimestampBehavior" and add a notes to "CModelBehavior" and "CActiveRecordBehavior", which should warn the developers.

@creocoder

The only way to avoid extra empty event handlers is not declaring them in "CBehavior::events()".

Or make it protected.

BUT we can NOT prevent it using any logic operators in "CBehavior::attach()".

We can, check implementation :-)

@creocoder

@klimov-paul Read the whole topic, see the implementation and especially tests. Are you still in doubt?

@klimov-paul
Yii Software LLC member

Using protected method approach still is not a good idea.
Let's image a developer, which creates his first active record behavior using the "CActiveRecordBehavior" as a start point.
What do think he will do? He will copy the blank methods from "CActiveRecordBehavior" inside his class. At this point it is easy to not notice the note about public/protected access or simply forget about it. So he leaves the "protected" modifier for the methods. Then he runs his code. What will he see? No errors, no warnings, but behavior does NOT working.
Current solution creates a risk of implicit errors in the code.

@creocoder

What do think he will do?

He will read method phpDoc. If not

Then he runs his code. What will he see?

That behavior does not working and he will read phpDoc.

All simple. And why AR should consume up to 8(!!!) times more memory and run up to 3(!!!) times longer because someone do not want read phpDoc? :-)

Current solution creates a risk of implicit errors in the code.

Try to change SomeController extends CController to SomeController in you apps. What do you see? Yii philosophy (i think) readily admits the risk of implicit errors in the code. Also i can show a lot more examples when this framework have an risk of implicit errors in the code. And this is normal. This is not Zend or Symfony with 1000 checks in methods body. And this is why its so fast. I hope Yii never change its philosophy. It's very simple, Yii knows what Coder make his job, not a monkey, as some other frameworks think.

So he leaves the "protected" modifier for the methods.

All know that protected methods cannot be invoked in the owner class. Are you sure Coder leaves it protected?

@klimov-paul
Yii Software LLC member

This entire issue has been risen because we were lazy.
We should create our behaviors from scratch using "CBehavior" and defining "events()" method every time, specifying only necessary event handlers. Instead we have created "CModelBehavior" and "CActiveRecordBehavior" defined blank methods for event handlers.
To fix this we should become eager: we should start to use "CBehavior" in general case. But you do not want to be so eager, you still want to be lazy and continue to use "CModelBehavior" and "CActiveRecordBehavior". I can understand you at this point. But you tell the developer should be eager to read all the docs carefully and makes all tests to ensure his code is working.

Now about your controller example:

Try to change SomeController extends CController to SomeController in you apps.

If I will do so this is not error, until I will invoke some method declared in the "CController". If I will create method:

class SomeController {
    public function actionSome() {
        $this->render('some');
    }
}

I will see a fatal error: undeclarater method.
If I do not use any other methods it is actually no necessaty of CController usage.
It is one thing to check everything, and totally another to skip the check, which already exists - it is like placing @ symbol at the code.

@creocoder, you have exposed a significant issue: memory consumption. It is a good catch, really, it is a good catch.
I, for example, always thought such extra callback links will not cause a significant memory usage, and I was wrong.

Now we just need a fix the problem, but not walk around of it. You attempt to use programming language syntax to specify your custom logic, while this syntax has not been desighned for it.

Calm down, review your code, try to view the problem from blank. If you will come to the same solution, I would not stop you.

@creocoder

Now we just need a fix the problem, but not walk around of it. You attempt to use programming language syntax to specify your custom logic, while this syntax has not been desighned for it.

Hmm... You read full topic? Its not my idea. But this is smartest idea we can do.

But you tell the developer should be eager to read all the docs carefully and makes all tests to ensure his code is working.

One time you will learn about this logic then all time you can be lazy.

Calm down, review your code, try to view the problem from blank. If you will come to the same solution, I would not stop you.

Modifing events() in all extensions (be always eager) VS protected in base classes (one time learn, all other time be lazy)? You really think anyone choose 1st ? :-)

You attempt to use programming language syntax to specify your custom logic

Try to remember @soap...

@samdark samdark was assigned Sep 23, 2012
@samdark samdark closed this in cfa1ebd Sep 23, 2012
@samdark
Yii Software LLC member

Since there's no better solution for a significant time and current one is BC and looks OK I've merged it.

Everyone involved, that's a very good catch and great job. Thank you.

@creocoder

@klimov-paul

Now about your controller example

I will see a fatal error: undeclarater method.

:-) No man. You just will see Unable to resolve the request "someController". You can try. So, implicit errors its all about Yii. And as i say this is OK. Just do not make them.

@creocoder creocoder referenced this pull request Sep 23, 2012
Closed

CHANGELOG for #1426 #1460

@klimov-paul
Yii Software LLC member

All right, fine.

@serebrov

Maybe a bit late, but I agree with @klimov-paul - changing method visibility from protected to public is not obvious and in my opinion it is a bad OOP practice. And Yii is a very good example of strong and correct OOP approach.

So I think that solution suggested by @slavcodev is better and fix issue for existing Yii classes.
As for Yii 2 - I think it is better to leave events() method empty. And the developer will be responsible for making a list of actually used events.

@grikdotnet

Yii is whatever, but NOT a good OOP approach. It violates MVC principles and forces dependencies in so many places that I don't eve want to list them. Learn OOP and take it easy. Yii is what it is, and it is an OOP with a strong smell of spaghetti-joomla.

@serebrov

@grikdotnet I can NOT imagine worse place then github issue to post your opinion about what Yii is.

@creocoder

@sebgoo

changing method visibility from protected to public is not obvious and in my opinion it is a bad OOP practice

It is not bad practice in our case. Imagine that we hit in 1% of situations when changing method visibility is good and best choose we can do.

So I think that solution suggested by @slavcodev is better and fix issue for existing Yii classes.

Hardcoding childen classes in parent class is worst thing we can do from OOP point of view.

As for Yii 2 - I think it is better to leave events() method empty.

This is not DRY.

P.S. I hope you will understand our decision when to weigh the pros and cons.

@apptous-seb

@creocoder I agree that current solution looks eleagant comparing to all other variants, but it is sill not perfect.
Any solution found here is a kind of hack and the best would be something that removes the root of the problem in behaviour classes and breaks BC.
If breaking BC is not an option then I would prefer a hack that stays hidden (most of the time) inside the framework code then hack which exposed to framework user.
So I do not compare solutions by "what is less bad programming practice" criteria, but by "what will fix problem and will be transparent for framework user most of time".
P.S. Anyway I understand that decision is made and will not argue anymore, just wanted to explain my opinion.
P.P.S. It is sebgoo (accidentaly posted under another account).

@creocoder

@apptous-seb Visibility changing in children classes from strong to weak is not a hack.

@klimov-paul
Yii Software LLC member

The solution breaks a BC!
In the Yii 1.1.12 any protected method can be assigned as event handler.
Try this:


class TestModel extends CFormModel {
    public $name;

    public function behaviors() {
        return array(
            'test' => array(
                'class' => 'TestModelBehavior'
            ),
        );
    }
}

class TestModelBehavior extends CBehavior {
    public function events() {
        return array(
            'onBeforeValidate' => 'beforeValidate'
        );
    }

    protected function beforeValidate() {
        die(__METHOD__);
    }
}


$model = new TestModel();
$model->validate();

I am not sure why such thing happens. I am using Yii 1.1.12 at PHP 5.3.3.
Can anyone confirm this?

@slavcodev

@klimov-paul you are first, this is what I was waiting for that option with protected/public confuses many.
In behavior need to make public methods.

class TestModelBehavior extends CBehavior {
    public function events() {
        return array(
            'onBeforeValidate' => 'beforeValidate'
        );
    }

    public function beforeValidate() {
        die(__METHOD__);
    }
}
@creocoder

@klimov-paul

The solution breaks a BC!
In the Yii 1.1.12 any protected method can be assigned as event handler.

Can't confirm. Anyway this solution not break BC. There is no places where:

I am not sure why such thing happens.

@serebrov

@slavcodev , @creocoder The code by @klimov-paul works and this means that it is possible to use protected methods as event handlers.
So someone can already have such methods. Current solution will prevent attaching event handlers for protected methods and this will break BC for behaviors with protected event handlers.

@klimov-paul I think protected methods work because event handeling is in CComponent and CBehavior extends CComponent, so CComponent is able to call protected method of CBehavior instance.

@creocoder

@sebgoo Someone can already have another BUGS in app. Who cares about it?

Event handlers should be PUBLIC.

@klimov-paul
Yii Software LLC member

The example I have provided works at my server. The protected event handler is actually invoked!
Although it can be result of some extensions I use or PHP settings. I am not sure if this is a native PHP behavior.
But if it is, someone else may already use this. If protected method can be an event handler, someone may use this to hide event handlers from the direct invokes for object.

In my example "beforeValidate()" handles event, however

$behavior->beforeValidate();

will produce an error.

@klimov-paul
Yii Software LLC member

This solution will produce a silent (without any message) error in case there is a proteceted event handler.

@creocoder

@klimov-paul Congratulations. You have not found anything better than to look for what is non-existent in practice code.

@klimov-paul
Yii Software LLC member

Perhaps I did.

@creocoder

@klimov-paul :-) You cant change public to protected in children. Your example is specially forged. TestModelBehavior will extends CModelBehavior in real applications! So stop spam this thread with garbage non-existent code.

@serebrov

@klimov-paul By the way your example works only if we extend CBehavior, but will fail with error if we extend CModelBehavior (can not change public to protected), so it is a very small chance to break someone's code.

@serebrov

Another idea - detect empty event handlers based on method length:

public function attach($owner)
{
    $this->_owner=$owner;
    if(($events=$this->events())!==array()){
        $reflector=new ReflectionClass($this);
        foreach($events as $event=>$handler){
            $method=$reflector->getMethod($handler);
            $length=$method->getEndLine() - $method->getStartLine();
            if($length)
                $owner->attachEventHandler($event,array($this,$handler));
        }
    }
}

...
/**
 * PHP docs
 */
public function afterConstruct($event) {}

The only drawback I see is code formatting - we need to define empty handlers in single line to get zero length.
Update: and can break BC if someone have public function afterConstruct($event) { do_everything_in_one_line(); }

@creocoder

@sebgoo You really think this solution is better??? More unreliable and ugly solution hard to think..

@serebrov

@creocoder Just another option, don't be so angry ))) and, by the way, thanks for your efforts - you do a great job by finding and trying to fix such issues as this and I am not trying to reject it.

@cebe
Yii Software LLC member

I see that this may cause problems with existing code in some rare cases. I think solution is good as it is but we should mention this change in UPGRADE notes. created an issue for that: #1550

@cebe cebe added a commit that referenced this pull request Nov 28, 2012
@cebe cebe UPGRADE comment on #1426
for details see discussion in #1426
issue #1550
bcaca7d
@Yiivgeny Yiivgeny added a commit to Yiivgeny/yii that referenced this pull request Feb 12, 2013
@Yiivgeny Yiivgeny Docfix for #1426 6a4b272
@Yiivgeny Yiivgeny referenced this pull request Feb 12, 2013
Merged

Docfix for #1426 #2101

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment