Posts: 346
Threads: 27
Joined: Sep 2010
2015-07-25, 21:54:33
(This post was last modified: 2015-07-27, 21:23:05 by Angryboy.)
GetSimple CMS "Hello World" Plugin
This plugin is a redux of the example plugin provided on the GetSimple wiki.
The aim of this project is to provide an example plugin with the same functionality as its predecessor, but with: - Full documentation
- Clearer design guidelines
- Ideal structure
- Language integration
- Avoidance of polluting the global variable/function namespace
- A portable library that acts as an SDK or devkit
so that plugin developers can use this example as a solid starting point for building their own (non-trivial) plugins.
The main idea is that the plugin's /lib/ directory contains some utility classes that help simplify plugin development, and these classes can simply be dropped into one's plugin and used as-is. These classes form a theoretical 'SDK', and if the SDK updates, a developer just needs to add those updated files to their /lib/ directory to immediately gain the benefits of the update. The developer should never need to edit the SDK library files themselves, and the SDK library should abstract them away from the hairier details and problems of building their plugin.
This is an experimental and open project, so I'm open to any and all suggestions. In particular, the utility classes are meant to cover a range of scenarios, including slugification, transliteration, saving XML files, creating directories, building admin user interfaces, etc..., so suggestions on what kind of methods/functions that plugin developers would want to have would be great.
Posts: 305
Threads: 15
Joined: Mar 2014
I like the idea of a 'Hello World' plugin for GS (especially since so many other libraries, CMS'es, and web-related stuff have one). I don't like the idea of having a 'Hello World' plugin in which the functions demonstrated are not in the GS core. In that sense I would rather consider this plugin to be just a plugin SDK in which an elaborate example is included about the use of the plugin. So basically I say "YES!" to all features you mentioned in the intro post, but "NO" to mixing the last feature with the preceding ones.
I like the OO approach, but expected to see in the code a working example with elaborate documentation about core functions like add_action and its different parameters; how to load scripts only on your own plugin page; how to implement your own hooks (so other devs can hook into them); how to use the init function; how to split your code into different files: etc. (for the record, I know how to do all this but took some effort finding out first).
Plugin Toolkits are great (and there already is one), I just don't think it's good to mix them up with a plugin that is meant to demonstrate the very basics of building a plugin.
Posts: 76
Threads: 7
Joined: Jun 2015
I really like this, it will give some much needed OOP-ness to plugins, but I don't think that dropping a lib folder into each plugin folder is the way to go, it should rather be built into the core so that every plugin just references once fileset instead of having it fragmented, but until that is in place, I will probably be using this or a modified version of it in the future.
Keep it up
Posts: 346
Threads: 27
Joined: Sep 2010
2015-07-27, 18:59:59
(This post was last modified: 2015-07-27, 19:29:04 by Angryboy.)
(2015-07-27, 07:54:01)Tyblitz Wrote: I like the idea of a 'Hello World' plugin for GS (especially since so many other libraries, CMS'es, and web-related stuff have one). I don't like the idea of having a 'Hello World' plugin in which the functions demonstrated are not in the GS core. In that sense I would rather consider this plugin to be just a plugin SDK in which an elaborate example is included about the use of the plugin. So basically I say "YES!" to all features you mentioned in the intro post, but "NO" to mixing the last feature with the preceding ones.
I like the OO approach, but expected to see in the code a working example with elaborate documentation about core functions like add_action and its different parameters; how to load scripts only on your own plugin page; how to implement your own hooks (so other devs can hook into them); how to use the init function; how to split your code into different files: etc. (for the record, I know how to do all this but took some effort finding out first).
Plugin Toolkits are great (and there already is one), I just don't think it's good to mix them up with a plugin that is meant to demonstrate the very basics of building a plugin.
Good insight, and I'm open to critique here.
The original Hello World plugin and the existing wiki illustrates a plugin simple enough to start grasping the basic functions, actions and hooks available to the developer. It doesn't show how to extend the plugin into something real-world usable (e.g. as soon as you start wanting to add i18n hashes, it introduces variable scoping issues and doesn't have a real solution beyond using globals).
Basically, every plugin developer is forced to make their own wrapper of sorts, or implement their own solution around the problems that immediately start appearing when extending the Hello World plugin. I'm not sure how best to illustrate the uses of all of the core functions without either showing an example plugin with its own wrapper (e.g a HelloWorld class), or with a plugin that uses global scope (or singleton objects, which can scare off some people).
So no matter how it is diced, plugin development with just the core functions is troublesome, and I suppose I don't know how best to introduce newcomers to the fun parts of development without detailing my own hacky solutions or abstracting them away from those implementation details entirely.
As for the functions not currently being core, I agree that it is not ideal. I've opened an issue on the GS github page about exposing a lot of these functions in a possible release, and shawn_a seems to have it penned for an eventual milestone. Until then, the best we can do to facilitate code reuse is to have a wrapper or polyfill for these methods.
And this example is unfinished and still in need of full documentation on the cases that you've said. I've submitted it now to see if there were any immediate no-nos as to its structure/conception (and again I'm glad to hear your responses).
(2015-07-27, 17:04:45)HelgeSverre Wrote: I really like this, it will give some much needed OOP-ness to plugins, but I don't think that dropping a lib folder into each plugin folder is the way to go, it should rather be built into the core so that every plugin just references once fileset instead of having it fragmented, but until that is in place, I will probably be using this or a modified version of it in the future.
Keep it up
Again, a /lib/ folder is not ideal for the reasons discussed above, but more complex plugins might employ other PHP libraries. A /lib/ directory gives a focal place to store all class-based scripts, and allows for easy autoloading integration (eventually).
I advise against using the example as-is now, because this is still experimental. People may vastly disagree with the design principles shown, and it would be a shame to have to rewrite your code later if this tanks.
I suppose the main thing to take away from both of your comments is that there is concern over what the example plugin should illustrate, and what the eventual SDK should consist of (and how separate it should be from the example). Obviously the SDK should be fully documented and elaborate more than the example plugin. How much should be shown in the Hello World example to keep it simple and accessible, but demonstrative enough to be a useful starting point?
Also, should this topic be moved to Developer Discussions?
Posts: 305
Threads: 15
Joined: Mar 2014
Quote:Also, should this topic be moved to Developer Discussions?
You could simply prefix the post title with [Discussion] or something.
Quote:The original Hello World plugin and the existing wiki illustrates a plugin simple enough to start grasping the basic functions, actions and hooks
I would even argue it doesn't do this completely.
Quote:I suppose the main thing to take away from both of your comments is that there is concern over what the example plugin should illustrate, and what the eventual SDK should consist of .
Yes, and this is much a matter of target audience. What is the level of skills of aspiring plugin developers? For me, it was zero. The only thing I knew about PHP was how to build forms & some other really basic stuff. And when you look at the code for WJ Notepad or some other plugins, you'll see it's pretty "low quality" (in terms of design patterns).
And although I would never use plugins like File editor, DY Lorem Ipsum or I18n language Menu Ex (because what these plugins do is so basic and offers, imo, no substantial benefit over hand-coding/ copy-pasting), it would be overkill to request from plugin authors that they become familiar with classes and OOP for such simple plugins, certainly when they start from zero (I learned more quickly because I already had an OOP/MVVM background in JS).
On the other hand, if I could I would totally urge plugin authors to build plugins which offer something substantial; I mean, plugins which truly save you a lot of effort (but ofc. also require more effort from the dev) eg. I18N, Itemmanager, Newsmanager (to name the truly biggest ones).
Same for custom functions, eg. I don't think $this->register(array(...)) is much more advantageous than register_plugin(...) .
Ideally, I think, there would be a 'Hello World' basic example, an intermediate, and an advanced example, perhaps we could even make use of the popular TodoMVC structure for the advanced example. This thread's initiative would be fit for the second/ third one.
Posts: 346
Threads: 27
Joined: Sep 2010
(2015-07-27, 20:32:03)Tyblitz Wrote: I would even argue it doesn't do this completely.
Fair enough (I have to concede the same).
(2015-07-27, 20:32:03)Tyblitz Wrote: Yes, and this is much a matter of target audience. What is the level of skills of aspiring plugin developers? For me, it was zero. The only thing I knew about PHP was how to build forms & some other really basic stuff. And when you look at the code for WJ Notepad or some other plugins, you'll see it's pretty "low quality" (in terms of design patterns).
And although I would never use plugins like File editor, DY Lorem Ipsum or I18n language Menu Ex (because what these plugins do is so basic and offers, imo, no substantial benefit over hand-coding/ copy-pasting), it would be overkill to request from plugin authors that they become familiar with classes and OOP for such simple plugins, certainly when they start from zero (I learned more quickly because I already had an OOP/MVVM background in JS).
On the other hand, if I could I would totally urge plugin authors to build plugins which offer something substantial; I mean, plugins which truly save you a lot of effort (but ofc. also require more effort from the dev) eg. I18N, Itemmanager, Newsmanager (to name the truly biggest ones).
Same for custom functions, eg. I don't think $this->register(array(...)) is much more advantageous than register_plugin(...) .
Ideally, I think, there would be a 'Hello World' basic example, an intermediate, and an advanced example, perhaps we could even make use of the popular TodoMVC structure for the advanced example. This thread's initiative would be fit for the second/ third one.
I also came in at next to 0 real programing experience besides very simple PHP scripts, and have largely picked things up since studying Computer Science, so I completely understand the "void" of sorts between the example plugin and the useful ones developed by our popular authors.
Fitting the 2nd/3rd description seems fine to me, then. From what I have seen, structuring a non-trivial plugin without any kind of OOP structure is like pulling teeth. When people make requests for the plugin's features later down the line, you get feature-creep, and it becomes insanely difficult to manage the growth of the codebase without encapsulating certain procedures into classes/singletons. On the flipside, I'd been hoping to allow the GSPlugin class to let users still benefit from OOP wrappers without having to implement them themselves (e.g. linking to other PHP scripts for procedures to execute, rather than having to create a global function or create their own class methods). Again, this is all experimental, so if it seems like a bad idea I am willing to scrap it.
Posts: 6,266
Threads: 181
Joined: Sep 2011
The example plugin is awful and dated.
Our plugin system is awful and dated....
3.4 does add a few fileio helpers so plugins do not have to deal with things like paths and security, or backups, or thumbs, but some things are still a pain in the ass, namely how plugins add sidemenus and their own pages with a mix of register_plugin params, hooks, and defined function callbacks.
These need to be easier and not so confusing. ( why does add_action sidemenu need to call createsidemenu )
wrappers for add_sidemenu, add_nav makes more sense.
registering callbacks for plugin pages should be easier than specifying which page i the register plugin params, which is only used for tab highlighting, which is again terribly confusing.
A bunch of params are passes around just to highlight stuff....
I think oop is a good way to go , but it needs to be extremely abstracted for ease of use.
and very easy to use, no advanced knowledge of autoloading or constructors.
plugins load.php action callbacks are confusing, you only get one, you can't add arguments, you have to merge in your own hooks and request checking, so whats the point. Stuff like this is what i find to be hard to understand.
This should be easy and can be
But we have no support in core for this stuff
we need action support, argument fetching support, plugins should not have to check POSTs, they should be passed from core to the plugin if the plugin registered a page etc.
Translation should be standard in a sample plugin
A standard config/collection file format and saving and reading with data path wrapping, so you only need to know your subpaths, and not have to deal with GSDATAOTHERPATH etc.
<items><item>
better way to register plugins, not a function call with arguments.
this plugin name =
this plugin id =
this plugin author =
non global pollution
Also a simple asset structure included with htaccess files
or a very minimal mvc struture
/assets/js,css,
/inc/
and a simple plugin for doing simple hooks or filters
a sample content filter, sitemap filter etc.
I like little plugins, that is the whole point, you want to send custom headers, why write the code and inject it somewhere, use a 5 line plugin.
I convert components into plugins all the time when they exceed more than say 5 functions or so.
( I use my component_hook plugin alot )
The biggest pain is global polution and unique naming, so here is where a class really shines, code reuse for the basic stuff that is in every single plugin.
Posts: 346
Threads: 27
Joined: Sep 2010
The GSPlugin class can be extended so that the plugin user wraps all of their methods in the one class and registers it as they would with the base GSPlugin class (i.e. init()).
Here is a sketch of the ideal methods for the GSPlugin class:
https://github.com/lokothodida/gs-plugin-sdk#gsplugin
Not all of them have been implemented yet, but it covers many of the cases you've mentioned (wrappers for sidebar/tab creation, abstracting creators away from being forced into OOP whilst still allowing for it, etc...).
Posts: 305
Threads: 15
Joined: Mar 2014
2015-07-28, 03:13:40
(This post was last modified: 2015-07-28, 03:14:18 by Tyblitz.)
Quote:But we have no support in core for this stuff
That's where the shoe pinches. It makes little sense to build layers upon layers of shiny abstraction on a foundation that is outdated. In the long(er) run this should be implemented in the core. I'm giving lots of feedback like "this & that should" & don't actually contribute much in the way of code, but that's because I don't know PHP well enough to mod the core.. Is there anyone else actively maintaining core except you, shawn_a?
Posts: 346
Threads: 27
Joined: Sep 2010
(2015-07-27, 07:54:01)Tyblitz Wrote: Quote:But we have no support in core for this stuff
That's where the shoe pinches. It makes little sense to build layers upon layers of shiny abstraction on a foundation that is outdated. In the long(er) run this should be implemented in the core. I'm giving lots of feedback like "this & that should" & don't actually contribute much in the way of code, but that's because I don't know PHP well enough to mod the core.. Is there anyone else actively maintaining core except you, shawn_a?
That depends on how the abstraction is done. If it is reasonable enough polyfill, then the underlying implementations can just be updated to reflect the changes to the core, and those using the abstractions won't have to change their code.
Plus, I don't see the GetSimple project making the kind of massive overhaul that would force existing plugins to be rewritten, because the plugins are the livelihood of the project. If something broke i18n or newsmanager, it would be a huge blow to the project unless the new system made them demonstratively trivial to redevelop.
Basically, we all want this stuff to be in the core. Shawn has said as much and has made it a milestone. But it's a pretty far milestone since he's dealing with a lot of other fixes in the existing code base. If we can form a portable solution that works, we could effectively end up doing the work for Shawn, and have it so that he (or any other core developer) just needs to port the SDK (or whatever is produced) into version X.
Posts: 6,266
Threads: 181
Joined: Sep 2011
Just me, but coding is not hard, the engineering IS.
Its easy to throw together a bunch of massive classes and SPL iterators, but neither are light, nor fast nor fit our objectives.
There are only several things in core that are worth the benefit of oop, for the rest its a hindrance.
Even MVC can make a simple project a massive distribution.
I think angryboys idea, is that the community needs something NOW to assist these things.
Also things like the UI class will never be in core, so that is really useful, it can be made to follow a style guide that will always work in GS backend.
Posts: 346
Threads: 27
Joined: Sep 2010
2015-07-30, 21:12:05
(This post was last modified: 2015-07-31, 01:12:03 by Angryboy.)
(2015-07-28, 04:18:07)shawn_a Wrote: Just me, but coding is not hard, the engineering IS.
Its easy to throw together a bunch of massive classes and SPL iterators, but neither are light, nor fast nor fit our objectives.
There are only several things in core that are worth the benefit of oop, for the rest its a hindrance.
Even MVC can make a simple project a massive distribution.
I think angryboys idea, is that the community needs something NOW to assist these things.
Also things like the UI class will never be in core, so that is really useful, it can be made to follow a style guide that will always work in GS backend.
I understand and agree with all of your points. It's difficult getting the balance between core functionality and bloat, and engineering it elegantly with respect to the current state of affairs must be nightmarish.
Having some kind of forward-compatible solution now would also definitely help with potential growing-pains if and when changes are made to the core.
In line with Tyblitz's comments on showcasing a Hello World plugin without external dependencies, I've created a branch that shows how this can be done in PHP 5.3+. That branch has a portable GetSimple environment on Vagrant along with it.
Posts: 305
Threads: 15
Joined: Mar 2014
(2015-07-30, 21:12:05)Angryboy Wrote: In line with Tyblitz's comments on showcasing a Hello World plugin without external dependencies, I've created a branch that shows how this can be done in PHP 5.3+. That branch has a portable GetSimple environment on Vagrant along with it.
I really like the new example, still clean & doesn't abstract away too much.
You think it's good to include a note like:
PHP Code: call_user_func(function($id) { include $id . '/index.php'; }, basename(__FILE__, '.php'));
// If you have to work with PHP 5.2 or older, replace the anonymous functions in the code // with named functions & their references, like so: // /* call_user_func('myplugin_init', basename(__FILE__, '.php')); function myplugin_init() { include $id . '/index.php'; } */
Posts: 346
Threads: 27
Joined: Sep 2010
2015-08-01, 20:21:33
(This post was last modified: 2015-08-01, 21:34:23 by Angryboy.)
I would want to do that, but it's not entirely honest because PHP 5.3+ anonymous functions are closures (and this is used for importing variables in the index.php file). There is no equivalence of a true closure in PHP 5.2 and below, so global variables (or class members) would be needed.
PHP 4-5.2 still have the old lambdas using create_function , but those require defining the functions within strings, and they still don't allow for variable importing.
Posts: 305
Threads: 15
Joined: Mar 2014
2015-08-02, 08:15:31
(This post was last modified: 2015-08-02, 08:19:23 by Tyblitz.)
(2015-08-01, 20:21:33)Angryboy Wrote: I would want to do that, but it's not entirely honest because PHP 5.3+ anonymous functions are closures (and this is used for importing variables in the index.php file). There is no equivalence of a true closure in PHP 5.2 and below, so global variables (or class members) would be needed.
PHP 4-5.2 still have the old lambdas using create_function , but those require defining the functions within strings, and they still don't allow for variable importing.
Yes, I'm aware. Open to suggestions for 'better' code (& explanation), just proposing something alike not to leave ppl with PHP 5.2- ( an already negligeable minority?) in the cold. (Maybe GS should move to support PHP5.3 instead of 2 because according to w3techs it has less than 0.1% usage
Posts: 346
Threads: 27
Joined: Sep 2010
2015-08-02, 11:33:33
(This post was last modified: 2015-08-02, 15:47:09 by Angryboy.)
I understand (and to note, you're already contributing to the PHP 5.2 discussion).
How about a very small wrapper for create_function :
PHP Code: // Wrapper if (!class_exists('Closure') && !function_exists('closure')) { function closure($params = '', $use = array(), $body = '') { return create_function($params, "extract($use); " . $body); } }
// Example Usage $i18n = closure('$hash', array('id' => $id), ' return i18n_r($id . "$hash"); ');
?
Not tested it yet.
Also, shawn: do you have a sketch of how you think an ideal plugin would end up looking once the refactoring is done (like in a GitHub issue or comment or something)?
Posts: 6,266
Threads: 181
Joined: Sep 2011
nope, just ideas not a hint of engineering or thoughts.
Your best bet for 5.2 is variable functions and extract() and create or callout the functions if they are not closures or classes.
or use variable functions and dynamically call them $$funcname, not sure what the best option is, i have not been following the conversation.
Or similar to how javascript handles event functions
on(event,function(e){...})
on(event,'funcname')
function.apply(this,$args)
or functionname(this)
call_user_func_array("functionname", array(this, rest of args));
myfuncname(this){
}
Posts: 6,266
Threads: 181
Joined: Sep 2011
I think the approach to this in core is also a rewrite from scratch.
Starting with engineering the entire class work required to emulate our existing functions.
Firstly by identifying why we have , I am just now starting to get an idea of the entire codebase as to how it's used and are doc blocking as much as I can. I have also ran some mapping on core to identify flows.
I have to start mapping all methods and this intent and actual use flow. And break it down into classes.
Right now we are roughly using classed files and methods, some of it can be easily cleaned up and turned into classes. But not much. So the first step is to run some phpdoc scripts or something better to generate method docs. Identify everything.
If you guys want to run with this go ahead, I really need the documentation done.
I'll look it over, and see how it looks. I am honestly not versed in php oop other than conceptual stuff, I have not really delved into what is avail in 5.3+
Posts: 346
Threads: 27
Joined: Sep 2010
2015-08-04, 01:06:18
(This post was last modified: 2015-08-04, 01:07:43 by Angryboy.)
Ahh, now I understand what you're doing. I thought you were one of the co-founding developers, which was why I was confused at why you needed to reverse-engineer so much of the codebase (as I'd assumed you were one of the people who originally built it!). Your approach seems like a very sensible way to go about it. I'm not sure how much help we can offer besides testing your implementations and pitching ideas? (unless you have small tasks that you'd like to delegate from time to time).
PHP's implementation of OOP is still not particularly attractive even after 5.3, but it at least provides more tools from 5.3/5.4 onwards.
I guess when it comes to changing the core it'd be a good idea to look at a number of different OO implementation sketches/patterns and see what appeals most. In terms of plugin functionality it might also be worth observing plugins for Javascript libraries (since they seem to be pretty good in terms of showcasing modularity without side-effects).
As for the gs-hello-world example, I'm going to a bit more experimenting to see what other kinds of plugins can be cleanly illustrated using 5.3+ closures.
Posts: 305
Threads: 15
Joined: Mar 2014
2015-10-30, 05:42:43
(This post was last modified: 2015-10-30, 06:04:23 by Tyblitz.)
@Angryboy, I've had another look at your Plugin SDK on Github;
I'm not a fan of the GSUI class to output the final HTML, but it could be very useful to create GetSimple-compliant pieces of markup, eg in my version of $ui->header you'd have direct control over the output (I like storing my markup in static html files), but not over the wrapper:
PHP Code: <?php $html = file_get_contents(GSPLUGINPATH . '/myplugin/nav-tmpl.html'); echo $ui->header('Admin Page Title', $html); ?>
Basically something that would work the same as creating a hook in the page's top nav to output HTML.
But except for that, great work on the GSUtils and GSPlugin classes! I really like all functions which simplify the GS interface (the asset registration utilities, hook stuff), and the filesystem functions. Also, the codebase is crystal-clear to me, nicely written.
Perhaps you could also consider the suggestion shawn_a and I discussed for limiting the asset loading to specific pages instead of only GSBACK, GSFRONT or GSBOTH (in any way you judge fit).
Other possible suggestions (for GSPlugin):
- making a call to the GSAPI for plugin info (with cache support)
- comparing local & remote version, with callback to exec if remote > local
Small typos in the API docs: mvfile has 'Deletes a file' as a description; GSUI input second - example in the code says 'type' => 'title' instead of 'type=>'text'.
Question: doing getfile with JSON parses the content.. as objects or nested arrays?
Thanks for developing this.
|