This question already has answers here:
The plugin generated X characters of unexpected output during activation (WordPress)
(25 answers)
Closed 4 years ago.
I want to create a wordpress plugin by just following the example listed here based on a class OOP architecture with an external setup object, and adapting the source code on my own way like this:
main plugin file:
<?php
/*
Plugin Name: My Plugin
Description: My Plugin
Version: 1.0
Author: drwhite
Author URL: drwhite-site
Plugin URL: drwhite-site/video-ad-overlay
*/
register_activation_hook(__FILE__, array( 'VAO_Setup_File_Inc', 'on_activation'));
register_deactivation_hook(__FILE__, array( 'VAO_Setup_File_Inc', 'on_deactivation'));
register_uninstall_hook(__FILE__, array( 'VAO_Setup_File_Inc', 'on_uninstall'));
add_action('plugins_loaded', array( 'VAO_Setup_File', 'init'));
class VAO_Setup_File{
protected static $instance;
public static function init()
{
is_null( self::$instance ) AND self::$instance = new self;
return self::$instance;
}
public function __construct()
{
add_action( current_filter(), array( $this, 'load_files' ));
}
public function load_files()
{
foreach ( glob( plugin_dir_path( __FILE__ ).'includes/*.php' ) as $file ){
include_once $file;
}
}
}
In my plugin root directory i have created a subdirectory called includes within i put the setup file to be loaded on plugin load called setup.class.php:
<?php
class VAO_Setup_File_Inc
{
public static function on_activation()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
$plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
check_admin_referer( "activate-plugin_{$plugin}" );
}
public static function on_deactivation()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
$plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
check_admin_referer( "deactivate-plugin_{$plugin}" );
}
public static function on_uninstall()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
check_admin_referer( 'bulk-plugins' );
// Important: Check if the file is the one
// that was registered during the uninstall hook.
if ( __FILE__ != WP_UNINSTALL_PLUGIN )
return;
}
}
When i activate the plugin i got an error like the following:
I have read several questions posted by other users here and this may be duplicated question, but any of suggested answer worked for me including:
Remove space from start of tags and even remove the php end tag: nothing changed
in wp_config.php file i set wp_DEBUG to true , but it doesn't show errors
I have converted the file to UTF8 (without BOM) nothing changed
Have you put the eye in the issue ?
You are getting this error because your plugin is generating a PHP error that is being outputted to the page and causing the headers sent error you see... The problem with your code is that your function
public function load_files()
{
foreach ( glob( plugin_dir_path( __FILE__ ).'includes/*.php' ) as $file ){
include_once $file;
}
}
is not being called in time, so
register_activation_hook(__FILE__, array( 'VAO_Setup_File_Inc', 'on_activation'));
is looking for a function that doesn't exist, inside a class that doesn't exist. Move your
foreach ( glob( plugin_dir_path( __FILE__ ).'includes/*.php' ) as $file ){
include_once $file;
}
outside the class altogether, and it'll load just fine. It may require you to rethink your use of
add_action('plugins_loaded', array( 'VAO_Setup_File', 'init'));
and the way your plugin is being created, but it's a step in the right direction. If you copy and paste the code from the link you got this code from, his code displays the same problem...
Related
I have used the wordpress site.
I used the child theme. After wordpress upgrade I received the following error
Fatal error: Cannot redeclare function name() (previously declared...
If I removed the function code it shows error for next function...
It shows the eror only the files which I include include("../../../wp-load.php");
I used some direct files Here I directly include the wp-load file
For these files it shows the error. If I convert these files as Template file then it working fine.
I don't know why the error occurs. Please help me.
Below is the code I used in my file
<?php
ob_start();
error_reporting(0);
include("../../../wp-load.php");
global $wpdb, $wp_hasher;
$fp_secquestion = $_REQUEST['fp_secquestion'];//'aaa';
$fp_secanswer = $_REQUEST['fp_secanswer'];//'aaa';
$fp_email = $_REQUEST['fp_email'];//'testuser5#st.com';
$user_login = sanitize_text_field($user_login);
if (empty( $fp_secquestion) || empty( $fp_secanswer) || empty( $fp_email) ) {
$log = 'validerr';
} else {
$user_data = get_user_by( 'email', trim( $fp_email ) );
if ( empty( $user_data ) )
$log = 'err';
}
echo $log;
So I am trying to run a simple if statement inside the wp-functions.php file and am using current_user_can. However I get PHP errors like: "Fatal error: Call to undefined function current_user_can() in..."
If anyone could take a look at my code, that would be much appreciated.
The code I am using is here:
global $current_user_can;
if ( current_user_can( 'manage_options' ) ) {
/* Admin User */
} else {
/* Member */
echo "<p>something</p>";
}
This usually happens because pluggable.php is not loaded by some reason. Try to add this before your function;
if(!function_exists('wp_get_current_user')) { include(ABSPATH . "wp-includes/pluggable.php"); }
It just checks if pluggable is loaded, and if not, it includes it.
if you want to check directly the role of the member, you can use this code:
global $current_user;
get_currentuserinfo();
if( !in_array( 'administrator', $current_user->roles ) ) {
//Do something
} else {
//Do something else
}
if you are creating a plugin, you must have to include like
if(!function_exists('wp_get_current_user')) { include(ABSPATH . "wp-includes/pluggable.php"); }// no need to add for theme functions
global $current_user_can;
if ( ! current_user_can( 'manage_options' ) ) {
// your stuff here
}
I'm trying to develop my first Wordpress plugin and I got staled in the very first stage. I'm trying to setup some options and database tables when the plugin is activated, but no luck. No matter what I do, the plugin activates, but the database is untouched and the options are not stored in DB. I tried echoing inside the constructor, but it seems that it never reaches it. I have debug activated in WP, but no error is being reported. The function is not being hooked. Does someone can spot what's wrong with my code?
Thanks for any help in advance.
class Myplugin {
private static $instance;
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
register_activation_hook( __FILE__, array( &$this, 'plugin_activate' ) );
}
public function plugin_activate() {
if ( version_compare( get_bloginfo( 'version' ), '3.8.2', ' < ' ) ) {
deactivate_plugins( basename( __FILE__ ) );
} else {
$rlm_rsvplus_options = array(
'db_version' => '1.0',
'event_name' => '',
'end_date' => '',
);
update_option( 'rlm_myplugin_options', $rlm_myplugin_options );
require_once( "includes/rlm_myplugin_db_setup.php" );//It never reaches this file.
}
}
}
$myplugin = Myplugin::get_instance();
Back to the WordPress documentation.
<?php register_activation_hook( $file, $function ); ?>
Parameters
$file
(string) (required) Path to the main plugin file inside the wp-content/plugins directory. A full path will work.
Default: None
$function
(callback) (required) The function to be run when the plugin is activated. Any of PHP's callback pseudo-types will work.
Default: None
Possible issue
If calling a function from a file that is outside of main plugin file, then the hook will not work as it is not pointing to the right file. FILE will point to the file where the code is written. So if you happen to include this part of code from elsewhere (another file - not the main plugin file) it's not supposed to work unless you point the right path.
Solution
A solution could be declaring a constant in the main plugin file.
your_main_plugin_file.php
define(PLUGIN_FILE_URL, __FILE__);
and then use it in your included file like so.
includes/some_file.php
<?php register_activation_hook( PLUGIN_FILE_URL, ['your_class_name_here', 'your_class_method_name_here']); ?>
or if you use functions instead of classes then do
<?php register_activation_hook( PLUGIN_FILE_URL, 'your_function_name_here'); ?>
The register_activation_hook call needs to be outside of the class itself.
Something like:
class Myplugin {
private static $instance;
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// do other stuff here
}
public function plugin_activate() {
if ( version_compare( get_bloginfo( 'version' ), '3.8.2', ' < ' ) ) {
deactivate_plugins( basename( __FILE__ ) );
} else {
$rlm_rsvplus_options = array(
'db_version' => '1.0',
'event_name' => '',
'end_date' => '',
);
update_option( 'rlm_myplugin_options', $rlm_myplugin_options );
require_once( "includes/rlm_myplugin_db_setup.php" );
}
}
register_activation_hook( __FILE__, array( 'Myplugin', 'plugin_activate' ) );
You can read more on the following tutorial by Francis Yaconiello about How to write WordPress plugin.
In order to work register_activation_hook OR register_deactivation_hook the functions should be in index file OR we need to specify the full path to the file argument.
Replace this:
register_activation_hook( FILE, array( &$this, 'plugin_activate' ) );
With:
register_activation_hook( FILE . 'plugin-main-file.php', array( &$this, 'plugin_activate' ) );
Here the point is register_activation_hook( $file, $function );
Here $file means Path to the main plugin file
Reference: https://codex.wordpress.org/Function_Reference/register_activation_hook
Thanks,
- Adi
I'm having a problem creating a class called API_Widgets in WordPress using the magic __call function. If I simple rename the file to derp.php and the class to API_Derp then it works without problems.
The following example has been stripped of everything unimportant to this issue (so if there is any error other than the specific fatal error specified in the third code block, ignore it).
Please keep in mind that I know the core.php or API class' __call works, as renaming the widgets.php or invoking another class works just fine.
core.php:
class API {
function __call( $method, $args ) {
$rmethod = "API_{$method}";
if ( !class_exists( $rmethod ) ) {
$lmethod = strtolower( $method );
require_once( "{$lmethod}.php" );
}
return new $rmethod( $args );
}
}
widgets.php:
class API_Widgets {
private $widgets = array();
function Add( $widgets ) {
if ( is_array( $widgets ) ) {
foreach ( $widgets as $widget ) {
if ( !in_array( $widget, $this->widgets ) )
$this->widgets[] = $widget;
}
} else
$this->widgets[] = $widgets;
}
}
api.php:
$api = new API();
$widgets = $api->Widgets(); // Fatal error: Class 'API_Widgets' not found in /home4/goldencr/public_html/wp-content/plugins/minecraft-api/api/core.php on line 25
//$widgets->Add( 'some_widget' );
Extending from comment:
Though not hinted at your question, it seems you may not actually included the widgets.php. Try to use absolute path to fix that:
require_once( __DIR__.DIRECTORY_SEPARATOR.$lmethod.".php" );
I have many plugins that I wrote for WordPress, and now I want to adapt them to MU.
What are the considerations / best practices / workflow / functions / pitfalls that I have to follow / avoid / adapt in order to 'upgrade' my plugins to support also Multisite installations?
For example, but not limited to:
Enqueue scripts/register
Incuding files (php, images)
Paths for custom files uploads
$wpdb
Activation, uninstall, deactivation
Handling of admin specific pages
In the Codex, there are sometimes remarks about Multisite in single function description, but I did not find any one-stop page that address this subject.
As for enqueuing and including, things go as normal. Plugin path and URL are the same.
I never dealt with anything related to upload paths in Multisite and I guess normally WP takes care of this.
$wpdb
There is a commonly used snippet to iterate through all blogs:
global $wpdb;
$blogs = $wpdb->get_results("
SELECT blog_id
FROM {$wpdb->blogs}
WHERE site_id = '{$wpdb->siteid}'
AND spam = '0'
AND deleted = '0'
AND archived = '0'
");
$original_blog_id = get_current_blog_id();
foreach ( $blogs as $blog_id )
{
switch_to_blog( $blog_id->blog_id );
// do something in the blog, like:
// update_option()
}
switch_to_blog( $original_blog_id );
You may find examples where restore_current_blog() is used instead of switch_to_blog( $original_blog_id ). But here's why switch is more reliable: restore_current_blog() vs switch_to_blog().
$blog_id
Execute some function or hook according to the blog ID:
global $blog_id;
if( $blog_id != 3 )
add_image_size( 'category-thumb', 300, 9999 ); //300 pixels wide (and unlimited height)
Or maybe:
if(
'child.multisite.com' === $_SERVER['SERVER_NAME']
||
'domain-mapped-child.com' === $_SERVER['SERVER_NAME']
)
{
// do_something();
}
Install - Network Activation only
Using the plugin header Network: true (see: Sample Plugin) will only display the plugin in the page /wp-admin/network/plugins.php. With this header in place, we can use the following to block certain actions meant to happen if the plugin is Network only.
function my_plugin_block_something()
{
$plugin = plugin_basename( __FILE__ );
if( !is_network_only_plugin( $plugin ) )
wp_die(
'Sorry, this action is meant for Network only',
'Network only',
array(
'response' => 500,
'back_link' => true
)
);
}
Uninstall
For (De)Activation, it depends on each plugin. But, for Uninstalling, this is the code I use in the file uninstall.php:
<?php
/**
* Uninstall plugin - Single and Multisite
* Source: https://wordpress.stackexchange.com/q/80350/12615
*/
// Make sure that we are uninstalling
if ( !defined( 'WP_UNINSTALL_PLUGIN' ) )
exit();
// Leave no trail
$option_name = 'HardCodedOptionName';
if ( !is_multisite() )
{
delete_option( $option_name );
}
else
{
global $wpdb;
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
$original_blog_id = get_current_blog_id();
foreach ( $blog_ids as $blog_id )
{
switch_to_blog( $blog_id );
delete_option( $option_name );
}
switch_to_blog( $original_blog_id );
}
Admin Pages
1) Adding an admin page
To add an administration menu we check if is_multisite() and modify the hook accordingly:
$hook = is_multisite() ? 'network_' : '';
add_action( "{$hook}admin_menu", 'unique_prefix_function_callback' );
2) Check for Multisite dashboard and modify the admin URL:
// Check for MS dashboard
if( is_network_admin() )
$url = network_admin_url( 'plugins.php' );
else
$url = admin_url( 'plugins.php' );
3) Workaround to show interface elements only in main site
Without creating a Network Admin Menu (action hook network_admin_menu), it is possible to show some part of the plugin only in the main site.
I started to include some Multisite functionality in my biggest plugin and did the following to restrict one part of the plugin options to the main site. Meaning, if the plugin is activated in a sub site, the option won't show up.
$this->multisite = is_multisite()
? ( is_super_admin() && is_main_site() ) // must meet this 2 conditions to be "our multisite"
: false;
Looking at this again, maybe it can be simply: is_multisite() && is_super_admin() && is_main_site(). Note that the last two return true in single sites.
And then:
if( $this->multisite )
echo "Something only for the main site, i.e.: Super Admin!";
4) Collection of useful hooks and functions.
Hooks: network_admin_menu, wpmu_new_blog, signup_blogform, wpmu_blogs_columns, manage_sites_custom_column, manage_blogs_custom_column, wp_dashboard_setup, network_admin_notices, site_option_active_sitewide_plugins, {$hook}admin_menu
Functions: is_multisite, is_super_admin is_main_site, get_blogs_of_user, update_blog_option, is_network_admin, network_admin_url, is_network_only_plugin
PS: I rather link to WordPress Answers than to the Codex, as there'll be more examples of working code.
Sample plugin
I've just rolled a Multisite plugin, Network Deactivated but Active Elsewhere, and made a non-working resumed annotated version bellow (see GitHub for the finished full working version). The finished plugin is purely functional, there's no settings interface.
Note that the plugin header has Network: true. It prevents the plugin from showing in child sites.
<?php
/**
* Plugin Name: Network Deactivated but Active Elsewhere
* Network: true
*/
/**
* Start the plugin only if in Admin side and if site is Multisite
*/
if( is_admin() && is_multisite() )
{
add_action(
'plugins_loaded',
array ( B5F_Blog_Active_Plugins_Multisite::get_instance(), 'plugin_setup' )
);
}
/**
* Based on Plugin Class Demo - https://gist.github.com/toscho/3804204
*/
class B5F_Blog_Active_Plugins_Multisite
{
protected static $instance = NULL;
public $blogs = array();
public $plugin_url = '';
public $plugin_path = '';
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
/**
* Plugin URL and Path work as normal
*/
public function plugin_setup()
{
$this->plugin_url = plugins_url( '/', __FILE__ );
$this->plugin_path = plugin_dir_path( __FILE__ );
add_action(
'load-plugins.php',
array( $this, 'load_blogs' )
);
}
public function __construct() {}
public function load_blogs()
{
/**
* Using "is_network" property from $current_screen global variable.
* Run only in /wp-admin/network/plugins.php
*/
global $current_screen;
if( !$current_screen->is_network )
return;
/**
* A couple of Multisite-only filter hooks and a regular one.
*/
add_action(
'network_admin_plugin_action_links',
array( $this, 'list_plugins' ),
10, 4
);
add_filter(
'views_plugins-network', // 'views_{$current_screen->id}'
array( $this, 'inactive_views' ),
10, 1
);
add_action(
'admin_print_scripts',
array( $this, 'enqueue')
);
/**
* This query is quite frequent to retrieve all blog IDs.
*/
global $wpdb;
$this->blogs = $wpdb->get_results(
" SELECT blog_id, domain
FROM {$wpdb->blogs}
WHERE site_id = '{$wpdb->siteid}'
AND spam = '0'
AND deleted = '0'
AND archived = '0' "
);
}
/**
* Enqueue script and style normally.
*/
public function enqueue()
{
wp_enqueue_script(
'ndbae-js',
$this->plugin_url . '/ndbae.js',
array(),
false,
true
);
wp_enqueue_style(
'ndbae-css',
$this->plugin_url . '/ndbae.css'
);
}
/**
* Check if plugin is active in any blog
* Using Multisite function get_blog_option
*/
private function get_network_plugins_active( $plug )
{
$active_in_blogs = array();
foreach( $this->blogs as $blog )
{
$the_plugs = get_blog_option( $blog['blog_id'], 'active_plugins' );
foreach( $the_plugs as $value )
{
if( $value == $plug )
$active_in_blogs[] = $blog['domain'];
}
}
return $active_in_blogs;
}
}
Other resources - e-books
Not directly related to plugin development, but kind of essential to Multisite management.
The e-books are written by no less than two giants of Multisite: Mika Epstein (aka Ipstenu) and Andrea Rennick.
WordPress Multisite 101
WordPress Multisite 110