Overriding plugin renderer moodle - php

I have site running Moodle 2.9.3+ and i was trying to customize the renderer of one existing plugin, so i found this:
How to override a renderer such that the functionality of the overridden renderer also remains available?
I added the following into the core_renderer.php of my theme
include_once($CFG->dirroot . "/course/format/topcoll/renderer.php");
but when i tried to do
class topcoll_local_renderer extends format_topcoll_renderer {
protected function section_header($section, $course, $onsectionpage, $sectionreturn = null) {
...
}
}
it runs, but it's not loading the modified function. Do I need to do anything else? the modified function is running with no issues when I alter the code directly into the plugin, but not like this. I would greatly appreciate any possible hint.
Thanks

You need to do two things to get a theme renderer to override a core renderer:
You need to edit the theme's config.php to add the line: $THEME->rendererfactory = 'theme_overridden_renderer_factory';
You need to name your renderer class 'theme_NAMEOFTHEME_format_topcoll_renderer' (and extend 'format_topcoll_renderer', as you have done).
The theme_overridden_renderer_factory works by extending the process of instantiating a renderer to look for a class that matches the name 'theme_NAMEOFTHEME_NAMEOFRENDERER' - as long as that class exists, then it should be used (otherwise the original renderer is used).
See https://docs.moodle.org/dev/Overriding_a_renderer for more details.

Hmmm are you sure there isn't an error in the script inclusion? If you have error_reporting off in your ini settings you won't see the E_WARNING PHP might be raising. Set this to on, or use require_once() instead. See the accepted response to this SO post for the differences: Difference between require, include and require_once?

Related

Expand / Override parent's method in constructor filter

I'm working on a WordPress project. There is a child theme implemented, and the parent one has a class to show authors' links. We have implemented a feature to support multiple authors per post, then we have our own methods for showing the proper links (not a single-author link, but many links joined depending on the authors count).
The complete scenario:
Theme: Newspaper
File: td_module.php (includes/wp_booster/td_module.php)
Class: td_module (abstract, many other classes in the parent theme inherit this one)
Method: get_author()
There are many other classes inheriting td_module in the parent theme, hence I cannot just extend td_module in my child theme since every change to the parent theme would be lost when the theme gets updated
Somehow I need to expand this method to show something different, but I don't want to change the method in the td_module class: even when it would be the fastest / most secure solution, this code would be overwritten on every theme upgrade.
This method is widely used in the Theme, that's why is that important to add some code here.
Why I still have some hope: There is a WP filter td_wp_booster_module_constructor, being called on the constructor of td_module class. The problem is that this class doesn't have any other filter on the method for showing the authors, but just the td_wp_booster_module_constructor filter call on the constructor.
The base PHP (and OOP) question: is it possible to expand / replace a method by using this filter in the constructor somehow?
Some code to clarify:
abstract class td_module {
...
function __construct($post, $module_atts = array()) {
...
// Can I change the get_author behavior by using this filter?
apply_filters("td_wp_booster_module_constructor", $this, $post);
// This is the only filter available in the entire class!
...
}
...
function get_author() {
$buffy = '';
// Code for generating author link ($buffy .= ...)
...
// This function doesn't have any apply_filter, there are no filters available
return $buffy;
}
}
Basically, you can't modify a class definition at runtime. The fact that you have a filter available on the constructor is a red herring and really not relevant.
Either the get_author() has some facility to change it's behaviour from outside class definition (in Wordpress parlance, filter and action hooks), or you simply cannot do it.
Outside of Wordpress, in an application with a proper dependency inversion container, you should probably do this by decorating the class and having all class consumer use the decorated class.
But since you have no way of telling class consumers to use one or the other, and a lot of code out of your control is presumably instantiating the class directly, something like this simply won't fly.
There is one extension, Runkit, that allows for changes of behaviour and definitions at runtime. there is even a method to modify a method definition. although I have not tried the extension, and do not know if it runs in an updated PHP runtime.
Note it is almost certainly a very bad idea to do this on production code, and that since you are dealing with code out of your control anyway you can't even be sure you would be changing the definition before it's used for the first time.
I've also found a repo for Runkit that says that it almost works on PHP 7, if you are absolutely convinced on going that way.
Your issue is that the base class is vendor code which you did not write.
So create a class in between your classes extending it!
Job done!
<?php
class SomeWordpressCrap
{
public function doSomething()
{
return 'something';
}
}
class YourAwesomeNewClass extends SomeWordpressCrap
{
public function doSomething()
{
return 'something better!';
}
}
class OneOfYourExistingClasses extends YourAwesomeNewClass
{
}
UPDATE So it turns out the OneOfYourExistingClasses is also vendor code, so the above solution will not work.
However! You could use Roave's "Better Reflection" lib, which you can find here https://github.com/Roave/BetterReflection
This will allow you to "Change the body of a function or method to do something different", which I believe is exactly what you need. Good luck!

How does WP file loading work? How does it work when used in WP MVC plugin?

I've created a video about one of the major problem that I have. I need to understand the file loading architecture, how it's done when writing a class in WP plugin development (how one class knows about the existence of another class), and how this compares with WP MVC file loading architecture. It's one big question, and I went through the various smaller questions that helped me arrived at the question summary in the video in the drive link below. I'm putting the plugin code in the Google Drive folder also: https://drive.google.com/open?id=1JVSSlkSJ5pCfNojRh6jen3ax2w-HZr5d
WP side:
plugin.php has this line: $wp_filter[ $tag ]->do_action( $args );
$tag = 'init'
This calls WP_Hook->do_action, then WP_Hook->apply_filters
WP MVC side:
This then calls MvcLoader->init, which then calls the needed load_controllers/libs/models/settings/functions (only the files under the bolded folder names will be loaded)
load_controllers will use the file_includer and make all files under the [plugin app path]/controllers be required
This means that any class under the specified folders above will be auto-required for the plugin that depends on WP MVC [tested]
This also explains why if one create a helper function, following the documentation, one would put the class under helper folder, but that doesn't get auto-loaded and one will get class not found error (because helpers doesn't get loaded/not in the list above)
Upon a closer inspection, it looks like only classes extending MvcController can load any helper files (base class MvcController is the only class that has load_helper() function). The helper files need to be named xyzHelper, under helpers folder, and needs to extend MvcHelper. In the child controller class, call $this->load_helper('xyz') and the base class will automatically try to load the helper class from the helpers folder. The controller class will now have a helper object named xyz (the class name, including underscores, without the _helper suffix). One can now have access to the helper class through the controller class (in the view classes also, etc. Used like $controller->xyz->function()) [tested]
How to get the above info:
Activate an example plugin from WP MVC and navigate to a public view
Put the breakpoint in mvc_file_includer.php require_file() [I actually just run the debugger without any breakpoint and there was a deprecated function warning at the above function. I didn't think to put a breakpoint at that function previous to seeing this, and I didn't know where to put the breakpoints previous to this realization]
Look at the stack trace and try to understand
Also, in WP MVC, there are other calls that will automatically include files
An example is render_view() - This will either call the controller's, or the helper's, render_view(), which will use the file_includer to find the desired file path, and call require on that file path. Therefore, it's more like this is a dynamic-loading of files that is being used.
Now, how did MvcLoader->init() get triggered?
In the main plugin's file: wp_mvc.php, depending on if it's in the admin/public functionality view, wp_mvc_load_global_functionality is called with the needed parameter. One of the actions in that function is add_action('init', array($loader, 'init')), which triggers the init hook for the MvcAdminLoader, which is a child of MvcLoader [tested by commenting out the add_action and it no longer works]

How to ignore 'redeclarations' of a type?

I'm building a 'core' for php. That 'core' is loaded before to load an application, for example Wordpress. Now, for example the 'phpmailerException' class is a part of Wordpress, but it is a part of the 'core' too. How can I ignore the fatal error: 'cannot redeclare...'. ¿There is way using php.ini or a special function?
Thanks
You should use a check with class_exists , and then declare your classes.
I don't know what do you mean by core.
I assume that's a php file your created.
If you load (by include/require) it at the beginning, then you can't prevent the cannot redeclare error. There is no way to skip such error by settings.
If the core is something like a plugin, then you could use class_exists, function_exists to check whether a class or a function is already declared.
It is also worth checking if WordPress is using include_once/require_once instead of include/require. If WordPress is using include_once/require_once you have to make sure you are using include_once/require_once as well. That way, you can be sure your classes will be included once

cakephp model class not found error while upgrading

I am trying to upgrade my CakePHP application from 1.3.6 to 2.2.4
I did all the upgrade steps based on official CakePHP upgrade documentation.
But i am struggling with this error:
Class 'Content' not found in
C:\wamp\www\cakephp-2.2.4\app\Controller\Component\OrderBaseComponent.php
on line 20
Argument 1 passed to Component::__construct() must be an instance of
ComponentCollection, none given, called in
C:\wamp\www\cakephp-2.2.4\app\Controller\Component\OrderBaseComponent.php
on line 17 and defined [CORE\Cake\Controller\Component.php, line 77]
For the first error make sure you always App::uses() every single class you use in your code.
So your Content (whatever class it is) must also be included before you can actually use it.
Only if it is a model you can just use ClassRegistry::init(), otherwise put sth like
App::uses('Content', '[TYPE]'); at the top of the file.
This second error is pretty self-explanatory!
Look into the outlined "CORE\Cake\Controller\Component.php" file and make sure your function does have the exact same arguments in your custom component:
public function __construct(ComponentCollection $collection, $settings = array()) {
//...
}
We don't have any relevant source code but generally you might add this at the top or your component file:
First:
App::uses('Content', 'Model');
And you might have to add something like in your init or construct method:
$this->Content = ClassRegistry::init('Content');
That should solve the first issue or give a clear explanation about what goes wrong. Actually this code was likely already not correctly independent functional.
I suspect it depends on the Model to be loaded already in another piece of code that's why it worked likely. A component should work not dependent on other pieces of code so adding the App::uses, App::import etc statements makes the code work always. For example when you re-use it in your other projects.
Second:
The second issue comes really with migration issues. Check that the model extends the Component class first.
Then make sure that if you implement a custom method __construct() but also init() for example that you check whether you should add a call to the parent. For example this applies to beforeFilter controller method.
public function beforeFilter() {
parent::beforeFilter();
}
Documentation and code example from: http://book.cakephp.org/2.0/en/controllers.html#the-app-controller
Relevant piece of documentation: http://book.cakephp.org/2.0/en/appendices/2-0-migration-guide.html#components

Is there an easy way to apply logging (e.g. Zend_Log) to Zend_Controller_Action_Helper_Redirector?

I've got an application which seems to be stuck in a redirect loop when I put it in a subdirectory of my server (e.g. blah.com/testing/ instead of blah.com/), despite sharing the same code. I think I've handled the redirection stuff incorrectly, but Apache's debugging output doesn't actually list the redirections, because they're being handled inside PHP.
Is there an easy way to attach a logger to the redirection function?
class My_Controller_Action_Helper_Redirector extends Zend_Controller_Action_Helper_Redirector
{
protected function _redirect($url)
{
$this->myPrettyLoggingFunction();
parent::_redirect($url);
}
}
if My_ namespace configured right, this plugin will be loaded by PluginLoader instead of default Zend_Controller_Action_Helper_Redirector
usage - standard way $this->_helper->redirector(...)

Categories