pradosoft / prado

Prado - Component Framework for PHP
Other
186 stars 70 forks source link

RFC - Better Composer Integration for extending PRADO #743

Closed belisoful closed 2 years ago

belisoful commented 3 years ago

PRADO should have some kind of self contained plugin module system that add blocks of functionality from self contained modules, portlets, pages, assets, etc.

Each Module will have a manifest.json defining it's internal properties and other info, like author information.

TPageService would need to be updated to handle alternative BasePaths. TControl method would add getting the Plugin Module that the control comes from TTemplate Would have a property to stop class/attribute validation for skin templates. This allows for objects in the skin that may not exist. If removing a plugin, correcting the skin shouldn't be needed. TSkinTemplate would be added for Skin Templates. TTheme would add dynamic events in case any behavior wants to modify the Theme loading behavior.

TPluginManager would be the main module that loads plugins. This uses application dyPreInit to load plugin modules prior to module initialization. TPluginModule would be a plugin module for defining plugin specific methods like reading and getting manifest parameters.

This adds a Plugins folder to the Application folder.

Use cases:

ctrlaltca commented 3 years ago

Since the modern PHP ecosystem is all about namespaces, composer and packagist, I'm a bit skeptical on creating a new plugin system that would be used only by PRADO. IMHO every feature plugin could live inside its own namespace in a composer package (prado/plugin-whatever), but it should not be a dependency of PRADO itself, instead it should be the application that requires it; The PRADO core should only provide all the needed hooks and functionality for these plugins to be loaded and used without the need of reimplementing that same exact code in every application. Still, I'm interested in seeing the possible ways this could be implemented.

belisoful commented 3 years ago

The hooks for implementing the plugin system are being placed into the system. I did realize that much. All plugins are additional functionality. PRADO would not be dependent on any plugin, the applications would include and depend on plugins. I'm looking to approximate the functionality of plugins for WordPress and Drupal, plug and play style. Such plug and play functionality is not possible when using composer on a php-prado application. (or is it?).

Here are some changes to support a more extendable PRADO. eg. TException allows an application to add their own error files, useful for individual plugins. TMap has dynamic events to patch into it for lazy loading application parameters (like from a database). Behaviors are in a better place now with properties at instancing and TBehaviorsModule. TTheme includes an update to be behavior friendly. __construct is properly chained. AutoGlobalListen is turned on for Application components and a few select TComponent children, like TUser. A feature possible (without memory leaks) because of TWeakCallableCollection. Modules creation raise dyPreInit on the module to ensure that any internal modules are preloaded into the application module system before the Init method is call. CLI enables application specific commands. (all on my branch).

This is exactly the commentary needed for this development. I saw the composer package system but it seemed overly complex for something as simple as installing a plugin and then having the application.xml/php reference it (or turned on via Web-UI). I would like to get to a point of being able to use a Web UI to turn on and off a plugin using app parameters, much like Wordpress or Drupal.

I have it automating plugin name spaces to have them be automatically used. Plugins can have and use their own individual namespaces. (I just made sure that works, and it does).

What is nice about the plugin system I'm working on is that it handles plugins with their own pages. not sure if this is exactly useful, but that is why development and testing comes first. Plugins are referenced by ID (set by the manifest.json) in the application.xml/php.

I'd like to look more at the composer and packagist template for ecosystem enhancement. Do you know of any resources that I should look at? I'll do my own searches too. Apparently, my mind is still working on old versions of PHP. It seems that the composer plugins for Prado include 3rd party sources but do not actually provide PRADO classes to access that functionality, no?

** Is it possible to add/use a composer plugin's own WebControls to an application? (sorry if my question seems simple. I don't know much about composer yet, still looking into it)

** if a user installs a new composer plugin, can it easily be turned on/off by PRADO via application.xml/php?

belisoful commented 3 years ago

also, does composer have plugin dependencies and process dependencies for proper load ordering of plugins?

belisoful commented 3 years ago

BTW, I am working on a Wordpress Integration plugin while concurrently working on the plugin manager. This allows for the actual usage of the plugin manager to see how it fairs. This is leading to many hooks for PRADO being found that are missing. It is an ambitious project and I don't know if it is viable yet. Given its self contained nature, the WordPress Integrator uses the new PRADO plugin framework I'm working on.

belisoful commented 3 years ago

I have a TTheme Behavior for integrating WordPress Themes that replicates some base WordPress functionality for rendering templates. It renders a main template and injects the WP html into the Page Controls and around the THead and TForm. Wordpress does not use modern namespaces. sigh. This is an interesting test to see how PRADO fairs.

belisoful commented 3 years ago

Here is a sample application.xml PluginManager Module template.

<module id="plugins" class="TPluginManager">
    <plugin id="test-plugin-2" />
    <plugin id="test-plugin-3" />
    <plugin id="test-plugin-1" />
    <plugin id="googleMapsEmbed" ApiKey="234243249283749879827429843"/>
    <plugin id="gdprCookieConsent" euOnly="0" MorePage="Privacy"/>
    <plugin id="devtools" />
    <plugin id="phpass" />
    <plugin id="wpIntegrator" ConnectionID="DB" WPDbParameterID="dbparam" WPUserManagerID="users" WPAuthManagerID="auth" LoginPage="Login" WPDirectory="/wordpress" />
</module>

This is the manifest.json I'm working on. Should it be changed to more like other platforms in various ways like multiple authors.

{
    "id": "googleMapsEmbed",
    "class": "GoogleMapsEmbedModule",
    "alias":"GMapsEmbed",
    "minpradoversion": "4.2.0",
    "paths": [
        {"namespace": "Plugin\\Portlets\\*"}
    ],
    "dependencies": [],
        "title": "Google Maps Embed API Plugin",
        "description": "This plugin encapsulates Google Maps Embed functionality within PRADO",
        "url": "http://github.com/",
        "version": "0.1.0",
        "tags": ["google", "maps", "maps embed", "location", "API", "earth"],

        "author": "Brad Anderson",
        "authoremail": "belisoful@icloud.com",
        "authorinfo": "a Daoist at Heart",
        "authorurl": "https://gcpdot.com",
        "notes": ""
}

any comments?

belisoful commented 3 years ago

I believe plugin pages could actually be useful by implementing the following

<com:TContent ID=<%= Prado::getApplication()->getParameters()->itemAt('PluginContentID') %>>
        ....
</com:TContent>

This allows the plugin pages to take on the standard application MasterClass Layouts without knowing how the masterclass is implemented. This could then be a parameter in the application.xml/php, settings, database or config.xml/php.

belisoful commented 3 years ago

I'm thinking of adopting the composer format of manifests for PRADO plugins for simplicity..

I think having PRADO plugins is a critical piece to competing with other platforms like Wordpress and Drupal, they have drop-in/plug-n-play modules. Having an ecosystem of interchangeable and competing modules is part of the reason PRADO doesn't have as much energy as these other platforms.

This Plugin Update should create PRADO as a platform for easy interchangable plugin development.

Having events and dynamic events in right places throughout PRADO would help plugin developers extend PRADO in the ways they are looking to do, similar to models used by other platforms. As more plugins are developed, more events and dynamic events would be added (if/when in the proper places), this would add flexibility and create a more competitive product.

Here are the features of the Plugin Update: -TPluginManager - -Creates a Plugin Namespace -reads manifests.json of each module -orders the plugins by dependencies, if they have any -loads and initializes modules -inserts plugin module error files, if specified -creates a namespace alias for each active module -uses the needed plugin namespaces -has a property for additional plugins for dynamically adding plugins from application parameters (conveyed via TParameterizeBehavior and TDbParameterModule) -uses < plugin /> rather than < module /> to indicate the differences in handling plugin ids and loading.

-TPluginModule -has its own namespace -has a static id that is referenced by the app config TPluginManager module -static ids could be "com.mycompany.OurModule" -has its own Pages directory, when in existence

TControl -adds getPluginModule that gets the path of the control and compares to plugin module paths. this allows plugin controls and pages to easily get their module.

belisoful commented 3 years ago

I had no idea how HUGE composer is. As far as plugins, I think there could be some improvement for integrating composer packages/libraries into PRADO.

belisoful commented 3 years ago

How about a composer.json ['extra'] field for 'pradoclass' that will instance the library as a module of that class?

Also extending the TPageService to include additional page paths if the main page path isn't found. Composer libraries could have their own Pages folder.

ctrlaltca commented 3 years ago

I'm not sure we can add new fields to composer.json without triggering an error. A composer plugin could handle the new fields, but i never looked into how to implement a plugin. As far as i see, other frameworks just create new packages and just register a new PSR namespace to add functionality that gets detected by the framework.

belisoful commented 3 years ago

That's what I was thinking about the "extra" field. From what I've seen, anything can be put in the "extra" field without issue. not sure about that though.

registering a new PSR namespace, imho, is not enough. Automatically loading a module into the application would be way better integration.

I'm thinking of having a TComposerManager that scans the installed libraries' composer.json and loads package modules from the "extra" field, like "pradoclass." Each composer PRADO module could implement a plugin module TModule subclass that looks for a composer Pages (for the TPageService::onAdditionalPages) for that specific library. This way composer PRADO plugins could be loaded automagically and have their own Pages served.

belisoful commented 3 years ago

https://alanstorm.com/magento-modules-and-composers-extra-configuration/

The extra field seems to be able to be used for other purposes.

Having worked on the plugin system a bit, I propose a Prado composer "extra" field called 'pradoclass' that loads the composer library/package Prado TModule. The loader could be its own module like TComposerManager

belisoful commented 3 years ago

This is part of the integration of composer libraries/packages with PRADO:

775

Composer libraries/packages should have their own pages that can be served when not found in the normal pages directory.

belisoful commented 3 years ago

I have been working on a TComposerManager module for PRADO. It reads the installed package/library composer.json files for their ['extra']['pradoclass'].

It instances any classes found.

The important key is that the TComposerManager also has definitions for packages matching the "name" of the package, and thus giving the pradoclass module application dependent property values, and order of instance.

< module id"*" class="TComposerManager"> < package name="pradosoft/prado" PropertyA="value1" PropertyB="valueB" /> < !-- all other packages load and initialize after the defined packages, -- > < /module> < !-- assuming pradosoft/prado had an ['extra']['pradoclass'] defined, which it doesn't -- >

Packages that are not explicitly defined in the TComposerManager configuration still instance because they are installed (via composer); after the explicitly defined packages in the configuration.

their module id is their composer name. having slashes in module id's should be fine.

TModule extends with a subclass that has its own Pages directory, where needed. any composer Pages Tpage has one TContent that has a standardized ID to an application parameter. this way applications can change the ID via application parameter. Each could have their own defined error file as well. Having a defined error file method would help people know about that feature and use it for their own proper error messages.

belisoful commented 3 years ago

if ['extra']['pradoclass'] cannot be filled in, then I suggest a standardized PradoModule class be defined in the root of the other vendor namespaces. This can be automatically and quickly processed.

The real question is: Should these composer package Prado modules be integrated into PRADO itself?

The code is about two pages and half of that is to initialize and load the modules.

belisoful commented 3 years ago

there are many places where events, both normal and dynamic, could and should be implemented through PRADO.

We can take WordPress, Yii, and Drupal (and their events/locations) as an example of where and how/where to add more PRADO events.

belisoful commented 3 years ago

Yii has an extra field in the composer.json for bootstrapping modules into the app.

I think using the same would be wise.

Also, having a composer Plugin for PRADO would be very helpful for automatically installing and uninstalling modules from composer. The Composer plugin would need to initialize the App then run the install/uninstall module base code.

I started a base Prado-composer extension here: https://github.com/belisoful/prado-composer-base

belisoful commented 3 years ago

Should the composer extensions be built into PRADO or should it be a separate module?

I have the TComposerManager completed and its working but it may be better being built into PRADO

belisoful commented 3 years ago

https://github.com/belisoful/prado/commit/e3eacbfd16763122b9959e9e86aea17f9e83fa8d

The composer extension bootstrap is built into the ApplicationConfiguration object when loading Modules.

belisoful commented 3 years ago

https://github.com/pradosoft/prado/commit/c3c92a552253b92bf00e762e3b7fe8dfb56d3c0c

belisoful commented 3 years ago

Are there any other ways to integrate composer extensions that crosses your mind?

belisoful commented 3 years ago

https://github.com/pradosoft/prado-composer-extension needs to be be added to packagist.org.

ctrlaltca commented 3 years ago

Done: https://packagist.org/packages/pradosoft/prado-composer-extension

belisoful commented 3 years ago

According to this: https://webtips.krajee.com/wp-content/cache/page_enhanced/webtips.krajee.com/setting-composer-minimum-stability-application/_index.html_gzip

the root package is the only package that responds to minimum-stability and prefer-stable. I think the only change to the prado-composer-extension is that the composer.json goes from

"require": { "pradosoft/prado" : "4.2.0" },

to "require": { "pradosoft/prado" : ">=4.2.0-alpha1" },

Put another way, only initial projects set up before 4.2.0 release need to have their own root composer.json with those configurations. Per documentation. when 4.2.0 is released, the main projects can revert the root composer properties back to base-line.

belisoful commented 3 years ago

Should there be some kind of automatic mechanism for a plugin module i18n?

ctrlaltca commented 3 years ago

That's a tricky one. TGlobalization currently only supports only one MessageSource for translations. A plugin could declare additional MessageSources, and TGlobalization could just cache them all and lookup on it. It will be quite harder to support the "autosave" feature, since TGlobalization should be able to determine what plugin is the correct one for each translation. This means it should detect if the file containing the localized string is part of a plugin and if that plugin declares a custom MessageSource. Additionally, MessageSources could be of different type, eg. database for the main app and php/gettext for plugins.

belisoful commented 3 years ago

I am looking through the I18N code. What if we tie together catalogs with sources. Plugins could register a catalog with Source/configuration in their composer name. If no source/config for a catalog exists, then use the default configuration. this could be an optimal solution. Plugin Pages would have their own catalog and could be set via page directive, Template instancing would need to be able to localize on a parents Catalog.

Alternative current functionality does exist, each page does have its own I18N via page name and folder names.