I want to make a simple modification in a PHP file located here in my parent theme:
wp-content\themes\sailing\inc\widgets\gallery\tpl\base.php
So I created the same folder structure in my child theme and did the modification I need in this file. I also copied/pasted all the PHP files needed to declare this widget.
wp-content\themes\sailing\inc\widgets\widgets.php
wp-content\themes\sailing\inc\widgets\gallery\gallery.php
wp-content\themes\sailing-child\inc\widgets\widgets.php
wp-content\themes\sailing-child\inc\widgets\gallery\gallery.php
What am I missing here ?
WordPress child themes are not working this way. The only files that you can override by using the same path in your child themes are the "basic" files: index.php, page.php, style.css... Mostly the template files.
When it comes to overriding functions or classes in a child theme. You've several ways to handle it:
re-declaring the functions/classes
duplicating the functions/classes
But it depends on how your theme is built and if it's "child theme" ready. Let's have a look with your widget issue.
If you open your widget declaration file within your parent theme, you'll see something like:
class Widget_Name extends WP_Widget {
...
CODE OF THE WIDGET
...
See: https://codex.wordpress.org/Widgets_API
The ideal case is you don't see the above lines first but:
if(!class_exists('Widget_Name')) {
class Widget_Name extends WP_Widget {
...
CODE OF THE WIDGET
...
Which means, you can just copy/past your file and that'll work just fine, you widget will override the parent one and no error will be thrown as the parent widget won't be executed. That's the "child theme ready" theme. Note that it's the same with functions (if(!function_exists('function_name')).
Don't forget to call your file from your child-theme/functions.php file as it won't be called by default.
Like:
require_once('path/to/your/widget_class.php');
Other way, if you don't have a class_exists call is to just duplicate the file, call it with the require_once. You should see an error as you're defining 2 times the same class. PHP won't let that happen, fatal error.
Just rename:
class Widget_Name2 extends WP_Widget {
And somewhere (most of the time at the end) of your file, look for register_widget( and edit the class name:
register_widget( 'Widget_Name2' );
That's not the most handy way as you'll have 2 times the same widget but that does work though.
So since #2Fwebd answer is kinda incomplete (as marked in the comment), here is a more complete answer (just to make it clearer than an answer and its comment. ) I've suggested an edit for his answer, but while it isn't accepted, here is a more complete answer :
WordPress child themes are not working this way. The only files that you can override by using the same path in your child themes are the "basic" files: index.php, page.php, style.css... Mostly the template files.
When it comes to overriding functions or classes in a child theme. You've several ways to handle it:
re-declaring the functions/classes
duplicating the functions/classes
But it depends on how your theme is built and if it's "child theme" ready. Let's have a look with your widget issue.
If you open your widget declaration file within your parent theme, you'll see something like:
class Widget_Name extends WP_Widget {
...
CODE OF THE WIDGET
...
See: https://codex.wordpress.org/Widgets_API
The ideal case is you don't see the above lines first but:
if(!class_exists('Widget_Name')) {
class Widget_Name extends WP_Widget {
...
CODE OF THE WIDGET
...
Which mean, you can just copy/past your file and that'll work just fine, you widget will override the parent one and no error will be thrown as the parent widget won't be executed. That's the "child theme ready" theme. Note that it's the same with functions (if(!function_exists('function_name')).
Don't forget to call your file from your child-theme/functions.php file as it won't be called by default.
Like:
require_once('path/to/your/widget_class.php');
Other way, if you don't have a class_exists call is to just duplicate the file, call it with the require_once. You should see an error as you're defining 2 times the same class. PHP won't let that happen, fatal error.
Just rename:
class Widget_Name2 extends WP_Widget {
Then, change your id_base in your parent::_construct to a unique id (like this :
parent::__construct( 'new_uniq_id', 'name of your widget', ...)
And somewhere (most of the time at the end) of your file, look for register_widget( and edit the class name:
register_widget( 'Widget_Name2' );
That's not the most handy way as you'll have 2 times the same widget but that does work though.
Hope it helps someone.
Related
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!
My parent theme has some bugs in a function I then want to override in my child theme.
The thing is it is neither pluggable and hookable. It is just defined like so :
// no if(!function_exists())
function parentThemeFunction() {
//some bad coding
}
// no add_action
And that is all. It is not defined in functions.php either, but in "parentTheme/directory/file.php"
So i heard about "runkit_function_redefine" and "runkit_function_rename", but it means i have to implement "runkit" library on my server.
So far, my only option is to edit the original function in the parent theme. With the risk to see my code overrided at the very next theme update.
Any idea?
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]
I have a Wordpress parent theme that my child theme is extending a lot of functionality from. My issue though is that when I want to use or extend a class from the Parent them in my child theme folder structure, I get errors of "class is not defined" or "class not found"
What is the correct way to use classes from the parent theme?
Here is an example of one of the classes:
class MyNewClass extends ReadAndDigestWidget {}
And that class will call more classes inside it that are from the parent theme.
Any help in understanding Wordpress and php more deeply is greatly appreciated!
PHP is not an OOP language by design so the results can be trivial at times. Here is a basic example of how inheritance works in PHP
class ChildTheme extends ParentTheme {
function parentDropDownMenu(){
parent::dropDownMenu();
//do additionall stuff
}
}
theme = new ParentTheme();
$theme->dropDownMenu(); // will call the parent function directly
$theme->parentDropDownMenu(); // will call the function override
Typically I wouldn't even override my parent's class methods in PHP but merely extend the class if I don't have complete control over the parent class. What if an upgrade takes place in wordpress and you override a must needed upgrade?
I am trying to override a function included in parent's theme functions.php.
The function I am trying to override lives in functions/widget-fblikebox.php
What I have in my parent's functions.php:
include("functions/widget-fblikebox.php");
functions/widget-fblikebox.php begins with this:
add_action('widgets_init', 'facebook_like_load_widgets');
function facebook_like_load_widgets()
{
register_widget('Facebook_Like_Widget');
}
class Facebook_Like_Widget extends WP_Widget {
I tried following this answer and this is what I did:
I created functions.php in my child theme and I entered there this:
<?php
// Add Facebook Like box Widget
include("functions/widget-fblikebox.php");
?>
I created a file in my child theme folder functions/widget-fblikebox.php that beginnis with this:
add_action( 'init', 'remove_facebook_like_load_widgets' );
function remove_facebook_like_load_widgets() {
remove_action('widgets_init', 'facebook_like_load_widgets' );
add_action( 'init', 'custom_facebook_like_load_widgets' );
}
function custom_facebook_like_load_widgets()
{
register_widget('Facebook_Like_Widget');
}
class Facebook_Like_Widget extends WP_Widget {
but I get an error
Fatal error: Cannot redeclare class Facebook_Like_Widget in /home/electronhe/over9000/www/wp-content/themes/truepixel/functions/widget-fblikebox.php on line 125
I googled this extensively and also tried everything that came to my mind to resolve this. I tried renaming the class in my child theme, using include_once in my child theme functions.php, etc. but to no avail. I you could please shed some light on this that would be greatly, greatly appreciated. The reason I am messing with this in the first place is that I would like to change the localization of the Facebook like box that comes with my theme (from mythemeshop, TruePixel) but it seems that there are no settings for the locale in the DB and the only idea of changing this that came to my mind was to redefine the class and hardcode proper locale in the URL of the iframe talking to FB.
Technically you can't override function declarations. Once it is declared, that's it.
To get around this, you can ask the parent theme author to support function overriding (they would wrap their function declaration in an if statement that checks to see if the function has already been declared) or you can just modify their file manually (but be careful of updating the parent theme.