I also considered using eval() first - but i find this a very hackish solution. I don’t like that one of the core classes has to be read in and the eval’ed all the time. You also have to alter the source code and insert the namespace statement.
I also considered using eval() first - but i find this a very hackish solution. I don’t like that one of the core classes has to be read in and the eval’ed all the time. You also have to alter the source code and insert the namespace statement.
All these solutions are awkward, it is much less messy to simply extend the classes you need. Your requirement of a class effectively extending itself is self imposed, find a simpler way ![]()
… then show one ![]()
class Html extends CHtml {
}
Simple! Ok it doesn’t meet your requirements of having the same name as CHtml,but imho that is EVIL and will trip you up in future. It’s confusing to have a class called CHtml that is not actually the real CHtml
You do know, that CHtml is used a lot all across the framework? So if you use a CActiveForm, it will use CHtml to render tags. You can not use your custom Html class there. So the whole point of this topic is, how to achieve this.
And my point is that there is no elegant way to achieve what you’re looking for apart from:
Override every class that uses CHtml directly (tedious, but this is the proper way to do it)
Edit the framework code to your needs and keep patching when CHtml changes upstream.
I can see that you really want to avoid both of these, but using eval and/or namespace trickery is far worse in my opinion. If you’re going down that route your might as well use runkit.
I just need it for one of my projects. I don’t want to abandon CActiveForm and i pointed out above, why i don’t want to alter framework files. And that i still want to be able to upgrade with least possible impact. I think, the solution does exactly this. I’m still interested to hear a better alternative.
Just copying over CHtml and doing local changes is not what i wanted. This way i can’t easily find my changes anymore. Therefore the namespace trick. All i have to do now on upgrade is copy & paste the CHtml code into my custom CHtml class into the right section (unless my pull request for CHtml gets accepted
).
But i still think, the described method could come in handy in several situations. E.g. what do you do if CActiveFinder needs an important fix in your project but yii does not have it in it’s official repo?
Can’t you use git for this? Fork the yii repo, add your changes and commit. When upstream changes, pull and resolve the merge conflicts. At least it’s automatic rather than copy and paste
That’s not really applicable to the project i have. And i also don’t want to maintain my own fork of yii just for this project. If yii upgrades you have the same problem again: You have to reapply your changes, maybe fix merge conflicts. The advantage of my solution above is, that you can use inheritance instead. So your changes are separated out and you just have to copy in the original CHtml code.
Don’t get me wrong, i don’t see this as good practice. It’s just a workaround for situations where you want to keep your project code totally separated of Yii - but still need to alter core classes somehow. It should hopefully be an exception.
Mike,
Here are my two cents. I’ve been trying to extend CHtml for some time now but this is pretty much impossible. I know what you mean by not wanting to maintain a huge chunk of code just to restructure a few tags, but since they are all static method any time a method you didn’t extend gets called if it ever calls other methods, it will do so via self:: and even if you extended the next called function it will not be called (self:: is not $this, and there is no this:: yet). So what happens is that you start extending more and more methods just to keep the callstack within your class and it just becomes a nightmare.
I gave up trying to do this. I’m grabbing the whole CHtml class and making it my b*tch own. I’ll just keep patch files handy for future Yii releases.
I found your post looking for "how to replace core classes", brilliant and easy 
Thanks!
JG
Can anybody see this being possible with Traits in PHP 5.4 combined with namespaces maybe.
EG:
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
We Are Doing In These Way
[color="#1C2837"][size="2"]STEP 1 ) Copy CHtml.php into protected/components/Html.php [/size][/color]
[color="#1C2837"][size="2"]STEP 2 ) Create CHtml.php under protected/components and extend it from Html class.[/size][/color]
[color=#1C2837][size=2]STEP 3) When the new Yii version is released, update Html.php[/size][/color]
Long term would it not be better if Yii core changed CHtml to some sort of Composite class patter. Where there are formatter classes that can be set as a setting and if nothing is set the default Yii core formatter class would be used. Long term this way we could support different doc types and standards IE: xhtml vs html5 and developers can customise the output of CHtml. CHtml is a static class/methods. I am not experienced enough to say what would be a good or bad architecture or how that would affect our options.
I am a little new to OOP and Yii, but from what I have learnt so far that sounds like something that could work.
That’s what i expect from Yii 2.0, too. But meanwhile we need to deal with Yii 1.x and that’s what the workaround is for. In 2.0 i’d like to see a pluggable CHtml class for bootstrap, foundation or any other CSS library.
Hi, I did just like you said here but it doesn’t seem to work, do you have something in your config file ?
'import'=>array(
'application.models.*',
'application.components.*',
'application.extentions.*',
'application.helpers.*',
),
Yes, you have to import your class. Please read my first post again carefully.
'application.components.CHtml',
Yes, I didn’t add this line of code in the import, because I thought that ‘application.components.*’, will do the job. But I ended up not using this method because I don’t want to update that file every time, and some errors might come up somewhere.
I actually quite like the idea of cloning and merging the Yii framework repository. If you simply added a namespace to the core CHtml class and made no other changes, then you’d never have to deal with merge conflicts. You could then write your own globally namespaced CHtml class and extend the core class directly within your projects in the way you’re already suggesting.
I don’t see any real downsides to that; it just means that the cloned version of the framework will only be useable in projects where you’ve overridden CHtml.
Is there any reason why CHtml use self:: to call static members of itself instead of CHtml::?
This means that my extended CHtml class need to override all entry points instead of just the protected activeInputField to set some properties of form fields used by screen readers depending on the status of the attribute. (required, has error, reference to the div containing the error message)
Eventually I think part of this may be useful in an upstream patch for CHtml but for now I like to use the extended CHtml approach.
I’ve solved the risk of forgetting this step by adding this code in index.php to be executed on servers in development mode (but not on production servers).
// Verify that components/core/Html is correctly updated
// when updating to a new framework.
$stock_chtml = file_get_contents(str_replace('yii.php', '', $yii) . 'web/helpers/CHtml.php');
// change CHtml => Html in the in-memory copy of the stock CHtml class
$stock_chtml = str_replace('class CHtml', 'class Html', $stock_chtml);
$core_html = file_get_contents('../protected/components/core/Html.php');
if($core_html != $stock_chtml) {
throw new Exception('protected/components/core/Html.php is not up to date.');
}
You need to update the path to your Html.php if your protected directory is not located in the parent directory of index.php.
This way my application will die with an error message if I forget step 3 rather than giving subtle errors. On my dev server, the two compared files will likely end up in some cache of my hard drive or in the OS, and thus not slow down the page views noticeable.
This topic was brought up by my colleague, so if anybody is still struggling with this, here is a working solution I came up with some years ago.
<?php
$cacheFile = Yii::app()->getRuntimePath() . DIRECTORY_SEPARATOR . 'yii_core_chtml_' . md5_file(__FILE__) . '_' . str_replace('.', '_', Yii::getVersion()) . '.php';
if(!file_exists($cacheFile)){
$file = Yii::getPathOfAlias('system.web.helpers.CHtml') . '.php';
$source = str_replace(['<?php', 'self::'], ['','static::'], file_get_contents($file));
$source = "<?php\nnamespace yii_core;\nuse Yii, CJavaScriptExpression;\n$source";
file_put_contents($cacheFile, $source);
}
require_once $cacheFile;
class CHtml extends yii_core\CHtml {
// here you can do whatever you wish, override any method etc.
// Above we replaced all self:: with static:: which activated LSB
}
Then you just override internal class mapping in application config, like this:
<?php
// let's say this is file containing Yii app config
return [
//...
'import' => [
'app.components.CHtml',
],
//...
]