Skip to content

Commit

Permalink
+ Implementing support for SMF-style file modifications (mods). (Mana…
Browse files Browse the repository at this point in the history
…gePlugins.php, Subs-MinifyPHP.php, index.php)

@ Notes:

(1) You may only edit files loaded via loadSource, including SSI.php and most files in the core/app folder.
(2) This is a very basic implementation for now. Hopefully, I can build upon this to make it a better mod system than SMF's.
(3) You need to add a 'mods.xml' file to your plugin folder, and just have it say things like this: <file name="Home"><operation><search position="after">loadTemplate('Home');</position><add>echo 'Hello, world!';</add></operation></file>.
(4) You can use CDATA tags if you want, but they're not actually taken into account. Meaning you can't really use </position>, </add> and </operation> strings inside your code. But then again... Why would you?
(5) This new feature enforces the use of app caching. So every time you enable or disable a plugin, pages should take a bit longer to load, to give Wedge time to actually re-patch files on your behalf. Good news is, if a file can't be patched (e.g. wrong file version or you edited it manually), conflicting plugins should be automatically disabled, and an error will be logged for admins to look into. Needs translations and stuff.
(6) As usual, I haven't tested much.
  • Loading branch information
Nao committed Dec 10, 2014
1 parent 8f04071 commit 16b5014
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 2 deletions.
17 changes: 16 additions & 1 deletion core/app/ManagePlugins.php
Expand Up @@ -369,6 +369,7 @@ function PluginReadme()
function EnablePlugin()
{
global $context, $settings, $maintenance;
static $flushed = false;

checkSession('request');

Expand Down Expand Up @@ -415,6 +416,13 @@ function EnablePlugin()
fatal_lang_error('fatal_install_error_maint_mode', false);
}

// Ensure PHP files will be flushed.
if (!$flushed)
{
$flushed = true;
clean_cache('php', '', CACHE_DIR . '/app');
}

// Hooks associated with this plugin.
$hooks_required = array();
$hook_data = array();
Expand Down Expand Up @@ -456,7 +464,7 @@ function EnablePlugin()
$hooks_provided[(string) $attrs['type']][] = (string) $provided;
}

// Add all the other hooks available
// Add all the other hooks available.
foreach ($context['enabled_plugins'] as $plugin)
{
$plugin = unserialize($settings['plugin_' . $plugin]);
Expand Down Expand Up @@ -1123,6 +1131,13 @@ function DisablePlugin($manifest = null, $plugin = null)
fatal_lang_error('fatal_conflicted_plugins', false, array($list));
}

// Ensure PHP files will be flushed.
if (!$flushed)
{
$flushed = true;
clean_cache('php', '', CACHE_DIR . '/app');
}

// Database changes: disable script
if (!empty($manifest->database->scripts->disable))
executePluginScript('disable', (string) $manifest->database->scripts->disable);
Expand Down
111 changes: 111 additions & 0 deletions core/app/Subs-MinifyPHP.php
@@ -1,6 +1,8 @@
<?php
/**
* This file manages the minification of PHP files.
* Note that, like anything not loaded through loadSource,
* you CANNOT edit it directly from a plugin!
*
* Wedge (http://wedge.org)
* Copyright © 2010 René-Gilles Deberdt, wedge.org
Expand Down Expand Up @@ -126,3 +128,112 @@ function find_next(&$php, $pos, $search_for)
return find_next($php, ++$next, $search_for);
return $next;
}

function wedge_parse_mod_tags(&$file, $name, $params = array())
{
$tags = array();
if (strpos($file, '</' . $name . '>') === false)
return $tags;
$params = (array) $params;

// The CDATA stuff, to be honest, is only there for XML warriors. It doesn't actually allow you to use </$name> inside it.
if (!preg_match_all('~<' . $name . '\b([^>]*)>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?</' . $name . '>~s', $file, $matches, PREG_SET_ORDER))
return $tags;

$empty_list = array();
foreach ($params as $param)
$empty_list[$param] = '';
foreach ($matches as $match)
{
$item = $empty_list;
$item['value'] = $match[2];
// No parameters? Just return now...
if (empty($match[1]))
{
$tags[] = $item;
continue;
}

// Now we'll retrieve the parameters individually, to allow for any param order.
foreach ($params as $param)
if (preg_match('~\b' . $param . '="([^"]*)"~', $match[1], $val))
$item[$param] = $val[1];
$tags[] = $item;
}
return $tags;
}

// Edit a source file from within a plugin.
function apply_plugin_mods($cache, $file)
{
global $context;
static $oplist = array();

// Phase 1: Waste no time if no plugins are enabled.
if (empty($context['enabled_plugins']))
return;

$this_file = $error = false;
foreach ($context['enabled_plugins'] as $plugin)
{
$mod = ROOT_DIR . '/plugins/' . $plugin . '/mods.xml';
if (empty($datalist[$mod]) && !file_exists($mod))
continue;
if (!isset($oplist[$mod]))
{
$data = file_get_contents($mod);
$oplist[$mod] = wedge_parse_mod_tags($data, 'file', 'name');
}
$tags = $oplist[$mod];
$ops = array();
foreach ($oplist[$mod] as $perfile)
if (isset($perfile['name']) && $perfile['name'] == $cache)
$ops[] = $perfile['value'];
if (empty($ops))
continue;
$ops = implode($ops);
$ops = wedge_parse_mod_tags($ops, 'operation');
if (empty($ops))
continue;
if ($this_file === false)
$this_file = file_get_contents($file);

// Phase 2: ???
foreach ($ops as $op)
{
$where = wedge_parse_mod_tags($op['value'], 'search', 'position');
$add = wedge_parse_mod_tags($op['value'], 'add');
if (empty($where[0]) || empty($add[0]))
continue;
$offset = strpos($this_file, $where[0]['value']);
if ($offset === false)
{
$error = true;
break;
}
$save_me = true;
$position = isset($where[0]['position']) && in_array($where[0]['position'], array('before', 'after', 'replace')) ? $where[0]['position'] : 'after';
if ($position == 'before')
$this_file = substr_replace($this_file, $add[0], $offset, 0);
elseif ($position == 'after')
$this_file = substr_replace($this_file, $add[0], $offset + strlen($where[0]['value']), 0);
elseif ($position == 'replace')
$this_file = substr_replace($this_file, $add[0], $offset, strlen($where[0]['value']));
}

// If an error was found, I'm afraid we'll have to rollback.
if ($error)
{
$context['enabled_plugins'] = array_diff($context['enabled_plugins'], $plugin);
log_error('Couldn\'t apply data from <em>' . $plugin . '</em> plugin to file <tt>' . $cache . '</tt>. Disabling plugin automatically.');
updateSettings(array('enabled_plugins' => implode(',', $context['enabled_plugins'])));
clean_cache('php', '', CACHE_DIR . '/app');
exit('Plugin error. Please reload this page.');
}
$error = false;
}

// Phase 3: PROFIT!
if (!empty($save_me))
file_put_contents($file, $this_file);
}
3 changes: 2 additions & 1 deletion index.php
Expand Up @@ -175,7 +175,7 @@ function loadSource($source_name)
if (isset($done[$file]))
continue;
$done[$file] = true;
if (defined('WEDGE_INSTALL') || !empty($db_show_debug) || strpos($file, 'getid3') !== false)
if (defined('WEDGE_INSTALL') || strpos($file, 'getid3') !== false)
$cache = APP_DIR . '/' . $file . '.php';
else
{
Expand All @@ -184,6 +184,7 @@ function loadSource($source_name)
{
copy(APP_DIR . '/' . $file . '.php', $cache);
require_once(APP_DIR . '/Subs-MinifyPHP.php');
apply_plugin_mods($file . '.php', $cache);
minify_php($cache);
}
}
Expand Down

0 comments on commit 16b5014

Please sign in to comment.