Request for version 2.0: no more static classes

I made a bunch of extensions to CHtml that I reuse across projects - and for each project, I derive a class from that, with project-specific Html extensions.

I’m discovering some ugly limitations of static classes in PHP.

Let’s say static class X extends B - so B is the base class, and X is the extension class.

If X overrides a static member, say, $var, of class B, the new value becomes inaccessible from B. As a work-around, the PHP team added late static binding - so you can use the static::$var syntax, as an alternative to self::$var - which will enable you to access the overridden X::$var from class B.

Unfortunately, this approach isn’t much better, since if X does not override $var, static::$var will fail in B.

In other words, the whole thing is sort of dodgy, and I don’t think the majority of developers even fully understand the meaning of late static binding.

There are other problems with static classes, but without getting into this, I’d like to suggest a way to avoid this in Yii 2.0, by turning helpers into real application components - which would also enable us to configure them.

Now, some of you are going to frown at this, but here it is…




<?php


function html()

{

  return Yii::app()->getComponent('html');

}



Yes, a flat function.

Now, in your views, you can say html()->beginForm() rather than Html::beginForm() … which, if you try it out, is actually easier to type, too.

Why would I advocate such a thing?

The thing I’m thinking is, flat functions make up the majority of PHP itself - which is the platform you’ve chosen. You’re not likely to run more than one framework at a time, and you’re not likely to write your own flat functions if you’re writing a Yii application. That means the global function namespace isn’t being used for anything at all.

So why would it not be okay for the framework to give you shortcuts, in the form of functions in the global namespace, for things like helpers? I don’t see a problem with that at all. You’d merely be using a namespace that isn’t useful for anything else, anyway.

I also would advocate shortening Yii::app() to simply app(), and Yii::app()->getUrlManager()->createUrl() to url() - as these functions would simply map to their OOP counterparts, you’re not losing any flexibility or functionality, you’re just getting a nicer, shorter syntax for the most commonly used features.

The fact of the matter is, the global namespace is there - it’s an integral part of PHP, and it delivers 90% of the functionality leveraged by the framework in the first place. Avoiding use of it, just so you can proudly declare, “it’s all OOP!”, is just denial - the majority of PHP is not OOP.

Looks a bit ugly at the first sight but still makes sense.

In this cookbook page, I recommended defining shortcut functions: http://www.yiiframework.com/doc/cookbook/31/

which does what you suggested here. Yes, in Yii 2.0 we will consider including them into the framework since we have namespace support.

Regarding CHtml and other static helper classes, you certainly find out the major problem with the static classes, i.e., they are hard to extend or configure. We actually have logged an issue about this: http://code.google.com/p/yii/issues/detail?id=121

On the other hand, having static classes makes reusable component/extension development a bit easier and standardized. I have yet to think of a better alternative to them in this situation.

I’m against configuring a lot of application components, because even with lazy loading, you still need to load the corresponding configurations for EVERY request, even though many of the components are not used at all during a single request.

We should discuss more about this.

I hear that, but for something like an HTML helper (like the URL manager and other essential components), it can come pre-configured - for the most part, people probably won’t add a lot of configuration (if any), but it gives you the opportunity to tweak certain settings more easily.

Which in the case of the HTML helper, I would say - extending the component with another class just to override a setting or two, probably results in more overhead than just adding another setting or two to your configuration file?

Another simpler and perhaps cleaner option, would be to have helpers available in your controllers. So from your view, you could simply obtain the helpers you need for that view - so, something like:




<?php $html=$this->helpers->html; // obtain the HTML helper ?>


<h1><?=$html->encode($model->title)?></h1>

...



This actually comes out one character less than the html() approach, and saves two function calls for every time you access the HTML helper.

It’s a little more explicit, in the sense that your template has to obtain the helper at the beginning. But sometimes being explicit is good, because it makes your code self-documenting - it takes out the mystery, and sometimes even opens up new possibilities.

For example, suppose I have several different HTML helpers - perhaps an HTML helper with specific extensions for my site, and another one with generic extensions for building out admin pages. This approach gives you that freedom.

Thinking about it, I actually like this approach a lot - perhaps this is even better than the global functions?

Hi,

i stumbled on this:

But my understanding was that on php versions before 5.3 the approach was that if X override $var, it was not accessable in functions of B, because of the lack of late static binding. Now you get $var of B with self and $var of X with static.

So to clear my understanding i come up with this litte test





class A {

	protected static $test = 'test';


	public function getSelfTest(){

		return  $this->getClass() . ' ' . self::$test . '\n';

	}


	public function getStaticTest(){

		return $this->getClass() . ' ' . static::$test . '\n';

	}


	public function getClass(){

		return __CLASS__;

	}

}


class B extends A{


	public function getClass(){

		return __CLASS__;

	}

}


class C extends A{

	protected static $test = 'mich';


	public function getClass(){

		return __CLASS__;

	}

}


$a = new A();

echo $a->getSelfTest();

echo $a->getStaticTest();


$b = new B();

echo $b->getSelfTest();

echo $b->getStaticTest();


$c = new C();

echo $c->getSelfTest();

echo $c->getStaticTest();



Retuns on my machine with php5.3.3:

A test

A test

B test

B test

C test

C mich

Therefore works the late static binding like i expected.

@Mindplay: have you discover another limitation?

Before php5.3 i also had this difficulty with CHtml,

but with php5.3 i don’t see a technical need of change here.

The only technical change would be to use static:: instead

of self:: in the static classes of yii.

I also think its cleaner to have the functions that

corresponding together in a named static class. I defined

a bunch of Helpers in static class all grouped by it purpose.

So if i need a function for e.g. string manipulating i don’t have

to remember the name of the specific flat function, but could

type StringHelper:: and the autosuggestion of my ide (netbeans) show

me all available methods. I think this is very handy…

By use of modern IDEs I don’t think its neccessary to shorten all

(system) calls.

Greetings from Germany

me23