Using functions for HTML generation

Yea. Many ways to shoot yourself in the leg.

In my oppinion type control which comes from the language itself is superior to the usage of PHPDoc hinting or alike. The problem with PHPDoc is that there is a possibility that it gets out of sync with the actual implementation. I know, IDEs can prevent this to an extent. Still, it can (and will) happen at one point. On the other hand, this can not happen with pure native PHP. If a use-clause or function is not defined, the code simply does not run through. This means for me that generally less PHPDoc is better, provided there is a native equivalent.

With that in mind I’m not sure if importing functions is a good idea: If I understood this right, for every function that should be importable in a view, the composer.json file had to be updated? That would always add an extra non-native step.

You’re talking about runtime errors? Because they’re working the same wherever you use Html::input() or $html::input().

PHPDoc/typehints could be replaced by assert($html instanceof Html::class) - at least on dev/test environment you should be able verify that type is correct.

I was rather generalizing from a bigger point of view. Of course you can assert everything. However, every step further is one line more to write. And time is money :slight_smile:

One thing I always appreciated about Yii (especially 2) is that the concepts and the implementing source code of the framework are pretty easy to understand. So for me the static class approach is as easy and bulletproof as can be, following this approach.

Just my 2 cent. :wink:

Honestly, I don’t care anymore about the time spent on typing. As a developer you spent most of the time reading the code, fixing bugs and making design decisions/solving problems. “Wasting” 5 seconds on writing assertion is pretty irrelevant in this context.

Yii (and the PHP world in general) seems to be moving in a different direction than you may expect. Yii 3 will rely mostly on DI and abstractions instead of using statics and direct class instantiation. What I’m proposing is basically DI for views. But unlike in case of DI for objects, there is no native and short syntax to specify view requirements in a raw PHP file. For object you can have public function __construct(RequestInterface $request){ /* ... */ } - this is a short, intuitive and type-safe way to specify requirements, but how to do the same in view context?

1 Like

Ok I understand your approach now. However, I don’t understand the benefits of DI for views. Maybe this is getting too off topic. After thinking about it I still can’t find real benefits in DI for views. Yes you can silently replace the view helper but the advantage in better testability doesn’t really work in a view, does it?

This may allow you to adapt views of extensions without completely overriding them. For example I may have requirement that all external links in my website should have target="_blank" rel="nofollow noopener". I can create my custom helper with a() method which will do that transparently, but if some extension uses static call to Html::a() in its view, my helper will not be used. I need to override the whole view to change one Html::a() to use my helper instead of basic one. With DI I could just inject my helper to extension view and extension will use implementation I prefer.

1 Like

https://github.com/yiisoft/view/issues/48 not to miss it.

Yep, didn’t thought about extensions… But I was not thinking in making them global, just thought it could be a core based self-dynamic way to do it on app demand, for functions and for any namespaces and so. I was really in stratospheric sci-fi here. Thanks for the answer @samdark.

I honestly believe that we should maintain the array configuration in the widgets and static helpers, I think that injecting everything through the container di would be equal to Yii2, in my opinion the views are only the representation of the result, and using static helpers would be fine, for On the other hand we would leave things like Yii2 like helpers and widgets that do not need a great design.

For the ide we can use the Snippets, for all static helpers it should work very well.

Sorry if I am not completely relevant, to much to read.

I suppose it is possible to have both static method convinience and flexibility of object.
Static method class calls Service methods, and we can replace the Service.

For example ,we have two Services, BootstrapService and SemanticUIService, both implements IHtmlService. Then inside, for example, Html::input we call Yii::getApp()->htmlService->input.

So it Html class becomes actually a Facade, instead staticaly linked to code.

There’s no such global anymore.

1 Like

Html static class could be initialized first somewhere with required service, for example, BootstrapUIService, this service could use other services, for example, CoreHtmlService, InlineCssService, AssetsService. So we could easily change functionality.

Html {
   private static $UIService;
   public static Init(IUIService $UIService) {
      self::$UIService = UIService;
   }
}

To optimize performance:

Html {
   private static $serviceLocator: IServiceLocator;
   private static $UIService: ?IUIService;

   // Initialization in bootstrap
   public static Init(IServiceLocator $serviceLocator) {
      self::$serviceLocator =  $serviceLocator;
   }

  private static ensureUIService() {
      if (! self::$UIService)
           self::$UIService = self::$serviceLocator->createOrGet(IUIService::class); 
  }

  public static alert($message) {
	self::ensureUIService();
	// main code
 }
}

This will still rely on global state, which Yii 3 tries to avoid.

The problem is that UI code is tightly coupled, and for me this was en issue, actually the only real issue with Yii.
Do you know better solutions?

Yes, I proposed it already:

This is completely the same with as I understand. We just use instance instead static class.

But in your solution exists one advantage. We can easily use another UIService for some part of appication. But @samdark proposed to use static class, thats why I did such a solution. But there is no problem to compose those approaches and give possibility to choose.

And that changes everything :D. You can have only one Html::$UIService at the time, and changing it in one place will affect any other places too. With local instances you have much more control and less unexpected side effects.