Order include file depending on inherit - php

Hi there people and greetings from Sweden!
I have a really tricky problem here. I'll try to show exactly what I mean.
I'm building a modulebased CMS, and some modules inherit from a parent module. My problem is that the parent modules need to be included before the "children".
I fetch moduleinfo from a XML file and store it in an array like this:
Array
(
[bloggy] => Array
(
[module_id] => blog
[module_name] => Blog
[module_desc] => Description
[module_url] => http://www.url.se
[author] => Dev Name
[author_url] => http://url.se
[version] => 1.0
[inherit] => core|twitter
[path] => /path/to/file
[dependon] => Array
(
[0] => core
[1] => twitter
)
)
I've done a explode on inherit and saved it into "dependon" as you see above. The problem now is, how can I sort which order to include the files. Every module inherits from core but if there is another module in the depenon array then the "child" module must be included after.
I hope you understand what I mean?
// Tobias

Look up "topological sort".

You could build you modules as classes and then use the __autoload magic function to automatically include/require all needed php files.
That way it's much less of a headache when you have complex dependencies.
Refer to the PHP manual for details on autoloading.

Just include all dependencies in your files. Try
// module1.php
require_once 'core.php'
// module2.php
require_once 'core.php'
require_once 'module1.php'
// module3.php
require_once 'core.php'
require_once 'module1.php'
require_once 'module2.php'
Including module3 will also include module2, module1 and core. You could leave out the core and module1 in module 3 and it would still load them all, but then you have to know what includes which.
Or use autoloading and don't bother about it.

Hmm I'll try to explain a little better.
I search the module folder for the xml info file and save the data into an array, I also save the path to the file in the array.
Then I use a foreach loop to include and instantiate the modules. My problem is that the parent modules must be instantiated before the "children".
I do not want to touch the core files when adding a new module, I need to use the hooks on a parent module.

Related

Drupal 8 custom module add php classes

I have created a custom Drupal 8 module that works as is with a custom block and block form to collect some info.
This is all good.
I also have a twig template that I want to render a twitter feed using a php feed class I bought. I just don't know how it integrate this into the module.
This is the setup for the class: http://austinbrunkhorst.com/demos/twitter-class/#setup
It contains two files:
ultimate.twitter.feed.php
and
tmhOAuth.php
Which is currently a require_once 'tmhOAuth.php'; in the head of ultimate.twitter.feed.php
According to the instruction I should be creating a php file that has this:
$options = array(
'screen_name' => 'FeedTestUser',
'consumer_key' => '...',
'consumer_secret' => '...',
'user_token' => '...',
'user_secret' => '...',
);
$twitter = new Twitter($options);
$twitter->PrintFeed();
Which I'm guessing is also a hurdle as twig files are not php
Any help with this is very much appreciated.
C
I would setup the class as a Service in your module. Your block will then implement that service and do the handling. You don't really want to use require_once() if you can avoid it, rather use Drupal constructs (in part so that if you reorganize things later Drupal will help find the files in their new location).
Place the class in your module's src directory, and add a namespace to the start of the file (assuming there isn't one there already). Then in your block's class file you should be able to add a use statement that references that name space (even better would be to use a dependency injection, but the details on that would get in your way here).
In your block class's build() method you then instantiate the class as described in your question, but instead of just letting the module print HTML, you can want to capture that HTML and place it into your block as markup. If the class allows you to do that without using a buffer, you should (but I didn't see anything in the docs to support that), and then attempt to theme the structured data. If not, you can use PHP's output buffering to capture its attempt to print:
ob_start();
$twitter->PrintFeed();
$content= ob_get_contents();
ob_end_clean();
Then place the generated markup into a render array:
return [
'my_twitter_block' => [
'#markup' => $content,
],
];
Create a custom block and add the result of PrintFeed() to the render array. Just as with any usual custom block. In the render array you can specify a template which should be used (if needed). If you wanna output pure html without any template you could use the '#markup' key.
Small example:
Your block render array:
return array(
'#theme' => 'name_of_your_theme',
'#some_twig_variable' => $twitter->PrintFeed();
);
your your_module.module file (in the root of your module folder):
function your_module_theme() {
return array(
'name_of_your_theme' => array(
'variables' => array(
'some_twig_variable' => some-default-value,
),
),
);
}
your name-of-your-theme.html.twig template (should be under your_module/templates):
{{ some_twig_variable }}
As far as using the class: I see no problem using a require_once for that matter (in your Block php file). Of course it's always better/nicer if you can require the library/package via the make file or composer and then use the autoloader, but if that's not possible just put it e.g. in your drupal root under /libraries/twitter or so and then require it. If you do it like that you have to check that library into your git repository obviously.
have you use ultimate.twitter.feed.php in your TwitterBlock.php file
If not then try adding this line before class block beginns:
require_once 'path/to/twitter_class/ultimate.twitter.feed.php';

Yii and PHPUnit Autoloaders conflict

This question is related to an earlier question I had, but I learned a bit more now, and that question became too messy, so I start a new question here.
I am trying to use Yii and PHPUnit at the same time, and try to get the Autoloading working. I found an Autoloader here that I am using for PHPUnit. I read here that I should register the Yii autoloader last.
With the following bootstrap file:
<?php
require_once(__DIR__.'/../tests/AutoLoader.php');
Toolbox\Testing\AutoLoader::registerDirectory(__DIR__.'/../protected');
require_once(__DIR__.'/../yii-1.1.14.f0fee9/yii.php');
print_r(spl_autoload_functions());
?>
I see that the autoloaders are in place in the right order:
[1] => Array
(
[0] => Toolbox\Testing\AutoLoader
[1] => loadClass
)
[2] => Array
(
[0] => YiiBase
[1] => autoload
)
However, I still can't import the class I need. It goes wrong in YiiBase in the function autoload in the line include($className.'.php');, which tries to include a class name without a path attached to it.
Why does Yii try to load a class without a full path to the file needed?
How can I make this combination to work?

Adding a version parameter to YII for application version

We are building a web application using Yii as the framework. Where would be a good location for us to put a version information array?
This version array is not the version of Yii but the version our application is on. This way we can use it global throughout the application. Example when deploy the application on our servers we can have a conditional that compares the required_php_version against the server's php version (phpversion()) to throw errors. This is just a simple example.
The array would consist of (with possibility to evolve later):
<?php
array(
'version' => '2.0.1',
'required_php_version' => '5.4.4'
);
?>
As far as I know, The best place to put your configurations in an application based on Yii, is main.php config file, which is situated in protected/config/main.php. But it is important to put your custom configurations in a right place. That is in params array. You can put your configs like below in config file:
'params' => array(
'webmaster' => 'YourEmail#example.com',
'required_php_version' => '5.4.1',
'my_app_version'=>'2.0.1.1',
'info_in_array'=>array(1,2,3,4,'so on ...')
// and so on
),
You can use these information in everywhere of your application like below:
Yii::app()->params['required_php_version'] //which returns 5.4.1 in this example.

Zend framework module library

I'm working on a Zend Framework (1.11) application that I am porting into modules so that the devs can plug and play various additions to our software and remove just as easily.
The issue I'm having (and don't seem to be able to find an answer for is) I have dependable library code, i.e my structure is currently this
- application
- (the standard ZF stuff)
- modules
- testModule
- Bootstrap.php
- controllers
- configs
- views
- library
- CoreStuff
- Class.php
- SpecialClass.php
- testModuleLibrary
- Class.php
- SpecialClass.php
And what I really want is this so that it is easier for the other devs to install modules to our system.
- application
- (the standard ZF stuff)
- modules
- testModule
- Bootstrap.php
- controllers
- configs
- views
- library
- Class.php
- SpecialClass.php
- library
- CoreStuff
- Class.php
- SpecialClass.php
Can I use the Autoloader in the module Bootstrap? or do I have to add it to my include path?
[EDIT]
This is my current module bootstrap, I've seen this code time and time again with my perilous Googling but it doesn't appear to make any difference
<?php
class Notifications_Bootstrap extends Zend_Application_Module_Bootstrap {
protected function _initLibraryAutoloader () {
return $this->getResourceLoader()->addResourceType('library', 'library', 'library');
}
}
I have resolved my issue with the following code. I literally have no idea why I didn't come up with this earlier :( :(
class Notifications_Bootstrap extends Zend_Application_Module_Bootstrap {
protected function _initConfig () {
set_include_path(implode(PATH_SEPARATOR, array(
dirname(__FILE__) . '/library',
get_include_path(),
)));
}
}
You can define your library paths in the application config file like:
This is example in YML
project:
bootstrap:
path: APPLICATION_PATH/Bootstrap/index.php
class: Bootstrap_Index
Autoloadernamespaces:
- Zend_
- Library1_
- Library2_
resources:
FrontController:
moduledirectory:
- APPLICATION_PATH/modules
......
Here is example in INI format
[bootstrap]
Autoloadernamespaces[] = "Zend_"
Autoloadernamespaces[] = "Library1_"
Autoloadernamespaces[] = "Library2_"
resources.FrontController.moduleDirectory = APPLICATION_PATH"/modules"
resources.FrontController.resetRouterOnEveryRequest = true
bootstrap.path = APPLICATION_PATH "/Bootstrap/index.php"
bootstrap.class = "Bootstrap_Index"
Regarding the project directory structure I advise you to use something similar to:
- application
- Bootstrap
- index.php
- Modules
- Mod1
- controllers
- views
- Mod2
- controllers
- views
...
- library
- Zend
- ...
- Library1
- ...
- Library2
- ...
Having done the directory structure you can have URLs like /:module/:controller/:action and keep all 3rd party code separate in its own pool Library directory
You may just use the modules' Boostrap.php.
Provide a function
protected function _initAutoload(){}
to make the lib available. Thus you may just make the module-developer do it, as it is part of the work developing a module to make it's resources loadable :)
I'm adding this response to hopefully both answer the original inquiry, as well as provide some clarification on a few items submitted here. I'm an active developer of modules in ZF 1.11, and use what I'm about to explain everyday in one of the several modules we maintain.
My apologies ahead of time for the length of this response. There are several items to cover and consider.
First, for the implementation.
The following snippet that was provided as far as I know worked ~1.8, and would not be what you want for 1.11.
<?php
class Notifications_Bootstrap extends Zend_Application_Module_Bootstrap {
protected function _initLibraryAutoloader () {
return $this->getResourceLoader()->addResourceType('library', 'library', 'library');
}
}
The following though would work fine provided a few key elements I'll explain below.
protected function _initLibraryAutoloader () {
return $this->getResourceLoader()->addResourceType('library', 'library', 'Library_');
}
You'll notice a slight difference in the third parameter ( the namespace ). The best explanation of this would be to possibly update the function to the following:
protected function _initLibraryAutoloader () {
$this->getResourceLoader()->addResourceType('library', 'library', 'Library_');
var_dump($this->getResourceLoader()->getResourceTypes());die;
}
Your output should be something similar to:
Array
(
[dbtable] => Array
(
[namespace] => Admin_Model_DbTable
[path] => /path/to/trunk/application/modules/admin/models/DbTable
)
[mappers] => Array
(
[namespace] => Admin_Model_Mapper
[path] => /path/to/trunk/application/modules/admin/models/mappers
)
[form] => Array
(
[namespace] => Admin_Form
[path] => /path/to/trunk/application/modules/admin/forms
)
[model] => Array
(
[namespace] => Admin_Model
[path] => /path/to/trunk/application/modules/admin/models
)
[plugin] => Array
(
[namespace] => Admin_Plugin
[path] => /path/to/trunk/application/modules/admin/plugins
)
[service] => Array
(
[namespace] => Admin_Service
[path] => /path/to/trunk/application/modules/admin/services
)
[viewhelper] => Array
(
[namespace] => Admin_View_Helper
[path] => /path/to/trunk/application/modules/admin/views/helpers
)
[viewfilter] => Array
(
[namespace] => Admin_View_Filter
[path] => /path/to/trunk/application/modules/admin/views/filters
)
[library] => Array
(
[namespace] => Admin_Library
[path] => /path/to/trunk/application/modules/admin/library
)
)
If you compare this to your previous "library" as the third parameter, you'll see in a second why it makes such an important difference.
To reiterate, at this point, you've declared a library type with the prefix of "Library_", which is translating to "Admin_Library". Now, to implement this, you'll have your library folder in your module just as you do in your main application module, with one slight adjustment. To have an Admin module specific controller action ( Admin_Library_Controller_Action ), you'll have library/Controller/Action.php, with a class name of Admin_Library_Controller_Action. This is what most find confusing at first, but it's very similar to the other resource namespaces you should be using in modules.
That concludes the technical explanation. If you read no further, you'll be able to have a library specific to your module and totally self-contained for easy copy/paste implementation of a reusable module.
Now for a few comments about some of the other responses I saw here.
infinity noted
"If you follow your structure you might end up with - Module1 (Lib1, Lib2) - Module2 (Lib1, Lib3) so you'll start duplicating the needed libraries."
This is technically correct, but really brings to light more issues in your actual development style if you find yourself routinely using the same libraries all over the place. We generally store these common items in another library, that we use as an external, identical to Zend, and is placed along side Zend in the main application library folder.
Provided you're already doing that, Where I believe some additional clarification might be needed is, if you're using the namespaces correctly, you absolutely don't have to worry about a collision of library class names. You may for a short period of time find that you need to have classes with identical code in multiple modules, but as soon as this happens, you should consider the common library in addition to Zend that I mentioned above.
My apologies again for the length of this response. I hope that it helps anyone who might come across this post in need of library implementation in modules.

joomla 1.7 : override a module helper.php

I want to override a helper.php file of a module, exactly
\administrator\modules\mod_quickicon\helper.php
what I want is to update the getButtons function in this file
what are the options I have to override this particular file ? its possible using a plugin ?
thanks
You can really only do a template override on the file in the tmpl folder called default.php. If you want to override helper.php, you're essentially rewriting the module itself. This isn't necessarily a bad thing, just copy the contents of the existing helper.php file into another file for backup, and hack away. The downside of doing this is that if someone updates the module, and you install that update, you'll have to redo your hack. Again, it's not that hard to work around. Just backup backup backup.
Some more thoughts:
You could also do the following:
1) Copy the module to a different folder, beginning with mod_ as well, but with a different name.
2) Modify the xml file, helper file, component file (and any others that are necessary) to account for the new name. Also do whatever you want to helper.php.
3) Go to extension manager and do a discover install of this new module
4) Go to module manager and make a new instance of the new module (using the same position as the old one).
5) Unpublish the module you're replacing
Doing this will keep you safe from update loss.
Here is what I've done with Joomla! 3.3.1 but it might be done with Joomla! 1.7, too:
Copy default.php from
/administrator/modules/mod_quickicon/tmpl/
to
/administrator/templates/YOUR_ADMIN_TEMPLATE_NAME/html/mod_quickicon/
Open the copied file and replace the line below
$html = JHtml::_('links.linksgroups', ModQuickIconHelper::groupButtons($buttons));
with these lines:
$myLinks = array(
'YOUR_QUICKICON_GROUP_NAME' => array(
array(
'link' => JRoute::_('index.php?option=com_YOURCOMPONENT'),
'image' => 'stack',
'icon' => 'header/icon-48-article-add.png',
'text' => JText::_('YOUR_QUICKICON_ITEM_NAME'),
'access' => array('core.manage', 'com_YOURCOMPONENT'),
'group' => 'YOUR_QUICKICON_GROUP_NAME'
)
)
);
$array = ModQuickIconHelper::groupButtons($buttons);
$array = array_merge($myLinks, $array);
$html = JHtml::_('links.linksgroups', $array);
That's it. You can edit or create /administrator/language/overrides/en-GB.override.ini and add these lines:
YOUR_QUICKICON_GROUP_NAME="The Group"
YOUR_QUICKICON_ITEM_NAME="The Item"
Enjoy!

Categories