Error trying to add parserHook to mediawiki - php

I'm running Mediawiki 1.23 and using the Syntaxhighlight plugin. 90% of the time, we use SQL as the specified language. E.g.,:
<syntaxhighlight lang="sql">
select 'foo';
</syntaxhighlight>
So I thought, "Why not just have a separate "sql" tag that invokes highlighter and sets the language to SQL? I.e.,:
<sql>
select 'foo';
</sql>
So I tried the following, but it doesn't work. I'm probably misusing PHP and I could use some help.
In LocalSettings.php:
require_once "$IP/extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.php";
In SyntaxHighlight_SeSHi.php I added the third setHook:
function efSyntaxHighlight_GeSHiSetup( &$parser ) {
$parser->setHook( 'source', array( 'SyntaxHighlight_GeSHi', 'parserHook' ) );
$parser->setHook( 'syntaxhighlight', array( 'SyntaxHighlight_GeSHi', 'parserHook' ) );
$parser->setHook( 'sql', array( 'SyntaxHighlight_GeSHi', 'parserHookSql' ) );
return true;
}
And finally in SyntaxHighlight_SeSHi.class.php I try to keep all the values that were coming in from the parser, but adding (or replacing) the "lang" value, and then call the original parserHook:
class SyntaxHighlight_GeSHi {
private static $initialised = false;
private static $languages = null;
public static function parserHookSql( $text, $args = array(), $parser ) {
$args['lang']='sql';
self::parserHook($text,$args,$parser);
}
public static function parserHook( $text, $args = array(), $parser ) {
global $wgSyntaxHighlightDefaultLang, $wgUseSiteCss, $wgUseTidy;
wfProfileIn( __METHOD__ );
self::initialise();
...
...
When I do this, the page renders but the rendered text from the sql tag is "UNIQ088c1443c530026e-sql-00000007-QINU", so I'm obviously doing something wrong.
So any help with my PHP, or maybe I'm extending mediawiki the wrong way... In either case, thanks in advance!

return self::parserHook($text,$args,$parser);

Related

PHP: How to check returned value of a function in another function

I have a function which returns a value that I'd like to check via another one. I can not echo it in the first function as far as I know, as it's used in a wordpress filter for body_class and outputted there.
So, how do I check what the returned value is in another function?
An example of the first function that returns $class, which is what I want to check:
function layout_class( $class ) {
// There is lots of more functionality here for the $layout variable
$layout = 'col-3cm';
$class[] = $layout;
return $class;
}
add_filter( 'body_class', 'layout_class' );
Now, this class decides whether or not to load a secondary sidebar template. So I would like to do:
function sidebar_dual() {
$layout = layout_class();
if (
( $layout == 'col-3cm' ) ||
( $layout == 'col-3cl' ) ||
( $layout == 'col-3cr' )
)
{ get_template_part('sidebar-2'); }
}
Since I can't echo out the first function, and don't want to write another function as it is quite big - how do I approach it? Is there an easy way to check a return value, similar to echo?
It looks like you need to refactor your code.
You just need to move the code that determines the layout into a separate function that you can call both inside the filter and inside the sidebar_dual() function:
function get_layout_mode(){
// There is lots of more functionality here for the $layout variable
$layout = 'col-3cm';
return $layout;
}
The filter function becomes:
function layout_class( $class ) {
$class[] = get_layout_mode();
return $class;
}
add_filter( 'body_class', 'layout_class' );
And in your sidebar_dual() function call get_layout_mode() instead of layout_class()
It is possible to work with your current code too, by splitting the string returned by WP's get_body_class() function into an array then checking if any of those 3 classes are present inside the array. I would go with the first option though, because it's clearer.
If you leave $class empty in your layout_class($class) then your function is invalid anyways. You need to declare something for the variable layout_class($class = null) or if you call it layout_class(null) but if you do not declare it, it will not work as the way you have it set up is required.
The rest of your code is okay.
Also, you have declared $class in your function, and you are using it as a variable within the function, I would rename your output ie
function layout_class( $class ) {
// There is lots of more functionality here for the $layout variable
$layout = 'col-3cm';
return $layout;
}

How to access a global $SiteConfiguration variable in PHP from everywhere?

I share the PHP code base across all pages and for each HTTP request I dynamically require the "/configs/$site/config.php" file. The file looks like this:
<?php
$SiteConfiguration = [
'site_title => 'Wiki for Developers',
'mysql_host' => 'localhost',
'mysql_db' => 'wiki-devs',
'articles_per_page' => 10,
/* ... etc ... */
];
?>
The problem I'm facing is that I can't quite access this variable from functions.
For example:
function DisplayArticles() {
echo "Displaying ".$SiteConfiguration['articles_per_page'];
}
It will print just Displaying and not Displaying 10.
How can I fix this and have my $SiteConfiguration accessible everywhere? Should I use a class? What's the best practice here?
put
global $SiteConfiguration;
in your function, you can find some more info at http://www.php.net/manual/en/language.variables.scope.php
Since you asked for best practice info: (simplest form)
class MySite{
public static function getConfig(){
return array(
'site_title => 'Wiki for Developers',
'mysql_host' => 'localhost',
'mysql_db' => 'wiki-devs',
'articles_per_page' => 10,
/* ... etc ... */
);
}
}
then in your code you can recall it with
$config = MySite::getConfig();
and use it. (obviously with a better, more descriptive name than MySite ;) )
Advantages:
your php class autoloader will automagically load it for you if setup correctly and your classes can be found, this means not worrying wether you passed a variable, not worrying about function argument placement and not tainting your functions with unnecessary arguments that don't help describe what it does.
you control exactly the access to this data and can make it so that not even your own functions can accidentally change this data, not even when calling other functions that would also need access to it.
in my opinion it beats globals and it beats passing via arguments since it's cleaner and you control the access to it in all forms. You can make certain attributes readonly/writable via specific getter/setter options, keep count of how many times it's accessed and whatever else you can think of.
Here's another case where a class for configuration would work great:
class Config {
private static $site_config = array( 'h' => 'Hello', 'w' => 'World');
public static function get( $key) {
return isset( self::$site_config[$key]) ? self::$site_config[$key] : null;
}
}
echo Config::get( 'h') . ' ' . Config::get( 'w');
This will output: Hello World
use global keyword
function DisplayArticles() {
global $SiteConfiguration;
echo "Displaying ".$SiteConfiguration['articles_per_page'];
}
Edit
You should try to avoid global variable.
A better practice would be to pass your array in parameter
function DisplayArticles( array $config ) {
echo "Displaying ".$config['articles_per_page'];
}
$SiteConfiguration = array( 'site_title' => 'Wiki for Developers',
'mysql_host' => 'localhost',
'mysql_db' => 'wiki-devs',
'articles_per_page' => 10,
/* ... etc ... */
);
DisplayArticles( $SiteConfiguration );
You can try something like this.
Your "siteConfiguration.php" file:
<?php
$SiteConfiguration = [
'site_title' => 'Wiki for Developers',
'mysql_host' => 'localhost',
'mysql_db' => 'wiki-devs',
'articles_per_page' => 10
];
return $SiteConfiguration;
?>
And this function:
function getConfigVar($var) {
static $config = array();
if( empty($config) ) {
$config = require("siteConfiguration.php");
}
return array_key_exists($var, $config) ? $config[$var] : null;
}
This function can also be modified to handle several configs.

Cannot redeclare class - Check if class already exists

I'm working with a script that is calling the same class twice and throwing the error:
Fatal: Cannot redeclare class urlConverter (/var/www/core/cache/includes/elements/modsnippet/23.include.cache.php:14)
I have tried placing the following code in:
if( !class_exists( 'urlConverter', false ) )
{
$urlConverter = new urlConverter( $modx );
}
However doing that the CMS I'm working with is reporting an Error 500 and haven't been able to see in the logs why it's throwing that error.
Does anyone know how to check if that class has already been declared correctly?
Edit:
I'm using a CMS so as a result the Class is housed in a Snippet and not an actual file. This is how they call their snippets:
$data['viewonlinelink'] = $this->modx->runSnippet( 'urlConverter', array(
'action' => 'encrypt',
'string' => http_build_query( $string ) ) );
I need to call that a second time to get a different result.
Edit2:
Here is urlConverter:
<?php
class urlConverter {
public $modx;
public function __construct( modX &$modx )
{
$this->modx =& $modx;
}
public function action( $scriptProperties )
{
return $this->$scriptProperties['action']( $scriptProperties['string'] );
}
private function encrypt( $str )
{
return $str;
}
private function decrypt( $str )
{
return $str;
}
}
}
$urlConverter = new urlConverter( $modx );
return $urlConverter->action( $scriptProperties );
Now from another script I call the following:
$data['viewonlinelink'] = $this->modx->runSnippet( 'urlConverter', array(
'action' => 'encrypt',
'string' => http_build_query( $string ) ) );
$data['confirmonline'] = $this->modx->runSnippet( 'urlConverter', array(
'action' => 'encrypt',
'string' => http_build_query( $reversed ) ) );
Which runs the function encrypt inside of my urlConverter class and I should receive two different results.
In using your updated code, change the class file to this:
<?php
if(!class_exists('urlConverter')){
class urlConverter {
public $modx;
public function __construct( modX &$modx ){
$this->modx =& $modx;
}
public function action( $scriptProperties ){
return $this->$scriptProperties['action']( $scriptProperties['string'] );
}
private function encrypt( $str ){
return $str;
}
private function decrypt( $str ){
return $str;
}
}
}
$urlConverter = new urlConverter( $modx );
return $urlConverter->action( $scriptProperties );
The redeclare class error is not caused by creating a new instance of the class, it's called by invoking the class operator on the same symbol. You're probably including the urlConverter class definition file multiple times.
If you cannot modify the way your class file is brought into the CMS (by using require_once or include_once), modify your snippet:
if( !class_exists( 'urlConverter' ) ) {
class urlConverter {
/* Build class */
}
}
Of course if you have anything else in that same file, you'll want to make sure it doesn't try to run twice.
It looks like the file that defines the class is being included more than once which is why you are getting the first error (cannot redeclare class).
/var/www/core/cache/includes/elements/modsnippet/23.include.cache.php line 14 seems to be what is including the class multiple times. If possible, change the include to include_once so you don't define the class multiple times.
To debug further (instead of seeing the 500 internal server error), try adding the following to your code as early as possible:
error_reporting(E_ALL);
ini_set('display_errors', 1);
Otherwise, check the servers error_log (Apache) and see if there is any useful information there about the 500 error.

Assetic AssetFactory::createAsset(array $input, ...) - $input doesn't accept \Assetic\Asset\* elements

I have a couple of methods which combine many CSS assets to one, minify them and dump them (cached) to a generated filename like "/assetic/6bad22c.css". I achieve this by making use of the following:
Currently I use AssetFactory
private static function getCssAssetFactory()
{
$fm = new FilterManager();
$fm->set('less', new Filter\LessphpFilter());
$fm->set('import', new Filter\CssImportFilter());
$fm->set('rewrite', new Filter\CssRewriteFilter());
$fm->set('min', new Filter\CssMinFilter());
$factory = new AssetFactory(self::getAssetBuildPath());
$factory->setFilterManager($fm);
return $factory;
}
and create an Asset via
public static function dumpStylesheets()
{
$asset = self::getCssAssetFactory()->createAsset
( self::$stylesheets
, array
( 'less' // Less CSS Compiler
, 'import' // Solves #imports
, 'rewrite' // Rewrites Base URLs when moving to another URL
, 'min' // Minifies the script
)
, array('output' => 'assetic/*.css')
);
$cache = self::getAssetCache($asset);
self::getAssetWriter()->writeAsset($cache);
return self::basePath().'/'.$asset->getTargetPath();
}
Here are the referenced methods:
private static function getAssetWriter()
{
if (is_null(self::$AssetWriter))
{
self::$AssetWriter = new AssetWriter(self::getAssetBuildPath());
}
return self::$AssetWriter;
}
private static function getAssetCache($asset)
{
return new AssetCache
( $asset
, new FilesystemCache(self::getAssetBuildPath().'/cache')
);
}
No magic here so far. My problem is, that the self::$stylesheets array, by definition, just contains path-strings to the assets. But I need to use real Assetic Assets like this way:
self::$stylesheets = array
( new Asset\FileAsset('path/to/style.css')
, new Asset\StringAsset('.some-class {text-decoration: none;}');
, new Asset\HttpAsset('http://domain.tld/assets/style.css');
);
But AssetFactory::createAsset() doesn't accept its own Assets. I need the possibility to use StringAsset because I have to change some values in CSS / JS, serverside.
Is there another way to achieve this other than using AssetFactory::createAsset()?
It looks like you may be able to essentially reproduce the inner workings of the createAsset method, short circuiting the parts that are giving you problems:
$asset = new AssetCollection(self::$stylesheets);
$filters = array
( 'less' // Less CSS Compiler
, 'import' // Solves #imports
, 'rewrite' // Rewrites Base URLs when moving to another URL
, 'min' // Minifies the script
);
$options = array('output' => 'assetic/*.css');
foreach ($filters as $filter) {
if ('?' != $filter[0]) {
$asset->ensureFilter(self::getCssAssetFactory()->getFilterManager()->get($filter));
} elseif (!$options['debug']) {
$asset->ensureFilter(self::getCssAssetFactory()->getFilterManager()->get(substr($filter, 1)));
}
}
$asset->setTargetPath(str_replace('*', self::getCssAssetFactory()->generateAssetName(self::$stylesheets, $filters, $options), $options['output']));
... all of that should adequately replace your call to createAsset(). If you have any workers that you've added to the assetFactory, then you'll need to implement that as well.

How to pass series of variables in function as array or regardless of their order?

I am sorry, that sounds like a noob question. I am trying to do this, maybe my question is not clear.
I want to be able to pass something like this:
make_thumbnail( array( 'width' => 60, 'height' => 40', 'title' => 'my image' ) );
Now the above line calls the function which already produces the thumbnails I have that no problem, but I want flexibility here. I mean my function has variables ordered like this:
function make_thumbnail($title,$width,$height) {
the code..
echo ...
}
Now you get what I want to do? I want to be able to pass the variables in any order.. they do not have to come in same order title, width, height.. i want to be able to specify the order when I call the function in template as I put in very first line.
I tried to make my question as clear as I can, but really could not find anything about it.
This sort of thing?
function make_thumbnail($myarray) {
$sometitle = $myarray["title"]
$somewidth = $myarray["width"]
$someheight = $myarray["height"]
}
Why not have the array as the function argument? e.g.
function make_thumbnail($argsArray) {
echo $argsArray['width'];
}
You can create variables within your function for each parameter
function make_thumbnail($argsArray) {
$width = $argsArray['width'];
$height = $argsArray['height'];
$title = $argsArray['title'];
// ...plug the rest of your original function here
}
Then your function will behave exactly the same, except you can pass in an array.
What you're asking for is a description of the Reflection syntax of PHP:
function callWithNamedParams( $funcName, array $args = null )
{
if( is_null( $args ) ) return $funcName();
$f = new ReflectionFunction($funcName);
$input = array();
foreach( $f->getParameters() as $param )
{
array_push( $input, #$args[ $param->getName() ] );
}
return call_user_func_array( $funcName, $input );
}
Use:
function myFunc( $foo, $bar )
{
echo "foo = $foo; Bar = $bar";
}
callWithNamedParams( "myFunc", array( "bar"=>1, "foo"=>2 ) );
You should get foo = 2; Bar = 1 as an output.
you need to define your logic to take any parameter as the one you want it to be. Array is the best thing you can use. But changing the parameters changes the signatures.
you are kinda implementing polymorphism but in a wrong way..

Categories