Joomla PHP error within page content - php

Warning: Parameter 3 to showBlogSection() expected to be a reference, value given in /home/smartsta/public_html/includes/Cache/Lite/Function.php on line 100
I'm getting the above error displaying within my content areas on my Joomla site all a sudden, any suggestions?
Update: No such luck finding access to defined file and directory within godaddy ftp file directory, ftp, or Joomal C-panel.
Within FTP, I cannot find access to this particular file to investigate what is on line 100.
Within the Joomla panel, in Global Configurations, I was able to toggle 'error message' to none for atleast this error to be hidden. Within the cache directory I do not see any options to get into the folder, though it displays.
I also see this at the bottom of that c-panel screen, but just links to a joomla help site, and within the fields I do not see described area to toggle 'ON or OFF'
"Following PHP Server Settings are not optimal for Security and it is recommended to change them:
PHP register_globals setting is ON instead of OFF
"
Update2!:
I've found the file in question, below is the code. Line 100 only states:
global $$object_123456789;
application/x-httpd-php Function.php
PHP script text
<?php
/**
* This class extends Cache_Lite and can be used to cache the result and output of functions/methods
*
* This class is completly inspired from Sebastian Bergmann's
* PEAR/Cache_Function class. This is only an adaptation to
* Cache_Lite
*
* There are some examples in the 'docs/examples' file
* Technical choices are described in the 'docs/technical' file
*
* #package Cache_Lite
* #version $Id: Function.php 47 2005-09-15 02:55:27Z rhuk $
* #author Sebastian BERGMANN <sb#sebastian-bergmann.de>
* #author Fabien MARTY <fab#php.net>
*/
// no direct access
defined( '_VALID_MOS' ) or die( 'Restricted access' );
require_once( $mosConfig_absolute_path . '/includes/Cache/Lite.php' );
class Cache_Lite_Function extends Cache_Lite
{
// --- Private properties ---
/**
* Default cache group for function caching
*
* #var string $_defaultGroup
*/
var $_defaultGroup = 'Cache_Lite_Function';
// --- Public methods ----
/**
* Constructor
*
* $options is an assoc. To have a look at availables options,
* see the constructor of the Cache_Lite class in 'Cache_Lite.php'
*
* Comparing to Cache_Lite constructor, there is another option :
* $options = array(
* (...) see Cache_Lite constructor
* 'defaultGroup' => default cache group for function caching (string)
* );
*
* #param array $options options
* #access public
*/
function Cache_Lite_Function($options = array(NULL))
{
if (isset($options['defaultGroup'])) {
$this->_defaultGroup = $options['defaultGroup'];
}
$this->Cache_Lite($options);
}
/**
* Calls a cacheable function or method (or not if there is already a cache for it)
*
* Arguments of this method are read with func_get_args. So it doesn't appear
* in the function definition. Synopsis :
* call('functionName', $arg1, $arg2, ...)
* (arg1, arg2... are arguments of 'functionName')
*
* #return mixed result of the function/method
* #access public
*/
function call()
{
$arguments = func_get_args();
$id = serialize($arguments); // Generate a cache id
if (!$this->_fileNameProtection) {
$id = md5($id);
// if fileNameProtection is set to false, then the id has to be hashed
// because it's a very bad file name in most cases
}
$data = $this->get($id, $this->_defaultGroup);
if ($data !== false) {
$array = unserialize($data);
$output = $array['output'];
$result = $array['result'];
} else {
ob_start();
ob_implicit_flush(false);
$target = array_shift($arguments);
if (strstr($target, '::')) { // classname::staticMethod
list($class, $method) = explode('::', $target);
$result = call_user_func_array(array($class, $method), $arguments);
} else if (strstr($target, '->')) { // object->method
// use a stupid name ($objet_123456789 because) of problems when the object
// name is the same as this var name
list($object_123456789, $method) = explode('->', $target);
global $$object_123456789;
$result = call_user_func_array(array($$object_123456789, $method), $arguments);
} else { // function
$result = call_user_func_array($target, $arguments);
}
$output = ob_get_contents();
ob_end_clean();
$array['output'] = $output;
$array['result'] = $result;
$this->save(serialize($array), $id, $this->_defaultGroup);
}
echo($output);
return $result;
}
}
?>

It is not exactly an error. It is a warning.
Suddenly? Perhaps you have upgraded/updated your PHP version. Or changed PHP configuration to "strict mode".
The message "expected to be a reference, value given" means the called function expected to receive a reference, not a value. Look:
$something = 9;
show_section($something);
// here you are passing a variable
// this will be accepted as a reference
show_section(9);
// here you are NOT passing a reference
// here you are passing a VALUE
When you pass "by reference", the function can change the variable value... in the example above:
function show_section(&$parameter) {
$parameter = 'changed!';
}
Note the ampersand symbol & before the $parameter - this is how we specify a function requires a REFERENCE.
AFTER the function call, in the example above, the variable $something value will be the changed! string.
The line throwing the error is NOT the "global" one. It is the next:
$result = call_user_func_array(array($$object_123456789, $method), $arguments);
The problem here is that the function is being called indirectly by using the "call_user_func_array" function.
A solution would be transforming all arguments into references. Suggestion:
foreach ($arguments as $count => $value)
{
$param = 'param' . $count;
$$param = $value;
$arguments[$count] = &$$param;
}
Put the code above in the beginning of the call function, right after the following line:
$id = serialize($arguments);
Give this a try!

Related

Upgrade Magento 2.2 > 2.3.2 - Type Error occurred when creating object: Magento\Framework\Communication\Config\Data

Upon Magento 2 upgrade 2.2.x -> 2.3.2, after running the deployment command php bin/magento setup:upgrade I am presented with the following error:
......
Module 'Magento_AdvancedPricingImportExport':
Module 'Magento_Directory':
Module 'Magento_Amqp':
Type Error occurred when creating object: Magento\Framework\Communication\Config\Data
This class exists, and it hasn't been overwritten with a plugin or preference. Why would this core class be throwing an issue?
So I found this article outlining others stuggle, I feel you all. Thankfully googla provided the fix that worked for me, I turned this into a nice module with a preference:
<preference for="Magento\Framework\Reflection\TypeProcessor" type="MY\MODULE\Framework\Reflection\TypeProcessor" />
Overwriting method getParamType:
<?php
/**
* Fix Magento 2.3.2 upgrade setup:upgrade
*
* #see https://github.com/magento/magento2/issues/22773
* #author Chris Rogers
* #since 1.0.0 <2019-08-30>
*/
namespace MY\MODULE\Framework\Reflection;
use Magento\Framework\Reflection\TypeProcessor as Original;
use Zend\Code\Reflection\ParameterReflection;
use Zend\Code\Reflection\DocBlock\Tag\ParamTag;
class TypeProcessor extends Original
{
/**
* Get the parameter type
*
* #param ParameterReflection $param
* #return string
* #throws \LogicException
*/
public function getParamType(ParameterReflection $param)
{
$type = $param->detectType();
if ($type === 'null') {
throw new \LogicException(sprintf(
'#param annotation is incorrect for the parameter "%s" in the method "%s:%s".'
. ' First declared type should not be null. E.g. string|null',
$param->getName(),
$param->getDeclaringClass()->getName(),
$param->getDeclaringFunction()->name
));
}
if ($type === 'array') {
// try to determine class, if it's array of objects
$paramDocBlock = $this->getParamDocBlockTag($param);
$paramTypes = $paramDocBlock->getTypes();
$paramType = array_shift($paramTypes);
$paramType = $this->resolveFullyQualifiedClassName($param->getDeclaringClass(), $paramType);
return strpos($paramType, '[]') !== false ? $paramType : "{$paramType}[]";
}
return $type;
}
/**
* Gets method's param doc block.
*
* #param ParameterReflection $param
* #return ParamTag
*/
private function getParamDocBlockTag(ParameterReflection $param): ParamTag
{
$docBlock = $param->getDeclaringFunction()
->getDocBlock();
$paramsTag = $docBlock->getTags('param');
return $paramsTag[$param->getPosition()];
}
}
The main difference is the return:
Original:
return $this->resolveFullyQualifiedClassName($param->getDeclaringClass(), $type);
Vs:
return $type;
Victims of Magento, you may continue your daily lives.

PHP CodeSniffer property not recognized

my sniff doesn't work and doesn't recognize the property private $testvar. I want to make a Doc-Block mandatory there.
When I run code sniffer, the process method doesn't seem to be used. I added some echos there before.
Does the token T_PROPERTY exist? I cannot find it on php manual http://php.net/manual/en/tokens.php
Yet, in the squiz lab source code T_PROPERTY is used.
<?php
/**
* Extension for the pear class comment sniff.
*
*/
/**
* Extension for the pear class comment sniff.
*
*/
class XYZ_Sniffs_Commenting_PropertyCommentSniff implements PHP_CodeSniffer_Sniff
{
private $testvar = 1;
/**
* Returns an array of tokens this test wants to listen for.
*
* #return array
*/
public function register()
{
return array(T_PROPERTY);
}
/**
* Checks the property comments.
*
* #param PHP_CodeSniffer_File $phpcsFile the file object
* #param int $stackPtr the stack pointer
*
* #return void
*/
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$find = PHP_CodeSniffer_Tokens::$scopeModifiers;
$find[] = T_WHITESPACE;
$find[] = T_STATIC;
$commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
if ($tokens[$commentEnd]['code'] !== T_DOC_COMMENT_CLOSE_TAG
&& $tokens[$commentEnd]['code'] !== T_COMMENT
) {
$phpcsFile->addError('Missing property doc comment', $stackPtr, 'Missing');
$phpcsFile->recordMetric($stackPtr, 'Function has property comment', 'no');
return;
} else {
$phpcsFile->recordMetric($stackPtr, 'Function has property comment', 'yes');
}
}
}
Thanks for your help :).
The T_PROPERTY token is only used when checking JavaScript files. It doesn't exist for PHP files.
For PHP files, you'll want to use the AbstractVariableSniff helper. Here is a sniff that checks comments of member vars: https://github.com/squizlabs/PHP_CodeSniffer/blob/master/CodeSniffer/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php
Notice how it extends PHP_CodeSniffer_Standards_AbstractVariableSniff and then only implements the processMemberVar() method. It leaves the processVariable() and processVariableInString() methods empty because it doesn't care about regular variables inside the code.
Also note that if you are writing commenting sniffs, the comment parser is completely different in the 2.0 version (currently in beta but due to go stable any week now). Take a look at the new version of the above sniff here: https://github.com/squizlabs/PHP_CodeSniffer/blob/phpcs-fixer/CodeSniffer/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php

Is there any way to compile a blade template from a string?

How can I compile a blade template from a string rather than a view file, like the code below:
<?php
$string = '<h2>{{ $name }}</h2>';
echo Blade::compile($string, array('name' => 'John Doe'));
?>
http://paste.laravel.com/ujL
I found the solution by extending BladeCompiler.
<?php namespace Laravel\Enhanced;
use Illuminate\View\Compilers\BladeCompiler as LaravelBladeCompiler;
class BladeCompiler extends LaravelBladeCompiler {
/**
* Compile blade template with passing arguments.
*
* #param string $value HTML-code including blade
* #param array $args Array of values used in blade
* #return string
*/
public function compileWiths($value, array $args = array())
{
$generated = parent::compileString($value);
ob_start() and extract($args, EXTR_SKIP);
// We'll include the view contents for parsing within a catcher
// so we can avoid any WSOD errors. If an exception occurs we
// will throw it out to the exception handler.
try
{
eval('?>'.$generated);
}
// If we caught an exception, we'll silently flush the output
// buffer so that no partially rendered views get thrown out
// to the client and confuse the user with junk.
catch (\Exception $e)
{
ob_get_clean(); throw $e;
}
$content = ob_get_clean();
return $content;
}
}
Small modification to the above script.
You can use this function inside any class without extending the BladeCompiler class.
public function bladeCompile($value, array $args = array())
{
$generated = \Blade::compileString($value);
ob_start() and extract($args, EXTR_SKIP);
// We'll include the view contents for parsing within a catcher
// so we can avoid any WSOD errors. If an exception occurs we
// will throw it out to the exception handler.
try
{
eval('?>'.$generated);
}
// If we caught an exception, we'll silently flush the output
// buffer so that no partially rendered views get thrown out
// to the client and confuse the user with junk.
catch (\Exception $e)
{
ob_get_clean(); throw $e;
}
$content = ob_get_clean();
return $content;
}
For anyone still interested in this, they've added it to Laravel 9
use Illuminate\Support\Facades\Blade;
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
https://laravel.com/docs/9.x/blade#rendering-inline-blade-templates
I just stumbled upon the same requirement! For me, i had to fetch a blade template stored in DB & render it to send email notifications.
I did this in laravel 5.8 by kind-of Extending \Illuminate\View\View. So, basically i created the below class & named him StringBlade (I couldn't find a better name atm :/)
<?php
namespace App\Central\Libraries\Blade;
use Illuminate\Filesystem\Filesystem;
class StringBlade implements StringBladeContract
{
/**
* #var Filesystem
*/
protected $file;
/**
* #var \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
protected $viewer;
/**
* StringBlade constructor.
*
* #param Filesystem $file
*/
public function __construct(Filesystem $file)
{
$this->file = $file;
$this->viewer = view();
}
/**
* Get Blade File path.
*
* #param $bladeString
* #return bool|string
*/
protected function getBlade($bladeString)
{
$bladePath = $this->generateBladePath();
$content = \Blade::compileString($bladeString);
return $this->file->put($bladePath, $content)
? $bladePath
: false;
}
/**
* Get the rendered HTML.
*
* #param $bladeString
* #param array $data
* #return bool|string
*/
public function render($bladeString, $data = [])
{
// Put the php version of blade String to *.php temp file & returns the temp file path
$bladePath = $this->getBlade($bladeString);
if (!$bladePath) {
return false;
}
// Render the php temp file & return the HTML content
$content = $this->viewer->file($bladePath, $data)->render();
// Delete the php temp file.
$this->file->delete($bladePath);
return $content;
}
/**
* Generate a blade file path.
*
* #return string
*/
protected function generateBladePath()
{
$cachePath = rtrim(config('cache.stores.file.path'), '/');
$tempFileName = sha1('string-blade' . microtime());
$directory = "{$cachePath}/string-blades";
if (!is_dir($directory)) {
mkdir($directory, 0777);
}
return "{$directory}/{$tempFileName}.php";
}
}
As you can already see from the above, below are the steps followed:
First converted the blade string to the php equivalent using \Blade::compileString($bladeString).
Now we have to store it to a physical file. For this storage, the frameworks cache directory is used - storage/framework/cache/data/string-blades/
Now we can ask \Illuminate\View\Factory native method 'file()' to compile & render this file.
Delete the temp file immediately (In my case i didn't need to keep the php equivalent file, Probably same for you too)
And Finally i created a facade in a composer auto-loaded file for easy usage like below:
<?php
if (! function_exists('string_blade')) {
/**
* Get StringBlade Instance or returns the HTML after rendering the blade string with the given data.
*
* #param string $html
* #param array $data
* #return StringBladeContract|bool|string
*/
function string_blade(string $html, $data = [])
{
return !empty($html)
? app(StringBladeContract::class)->render($html, $data)
: app(StringBladeContract::class);
}
}
Now i can call it from anywhere like below:
<?php
$html = string_blade('<span>My Name is {{ $name }}</span>', ['name' => 'Nikhil']);
// Outputs HTML
// <span>My Name is Nikhil</span>
Hope this helps someone or at-least maybe inspires someone to re-write in a better way.
Cheers!
I'm not using blade this way but I thought that the compile method accepts only a view as argument.
Maybe you're looking for:
Blade::compileString()
It's a old question. But I found a package which makes the job easier.
Laravel Blade String Compiler renders the blade templates from the string value. Check the documentation on how to install the package.
Here is an example:
$template = '<h1>{{ $name }}</h1>'; // string blade template
return view (['template' => $template], ['name' => 'John Doe']);
Note: The package is now updated to support till Laravel 6.
I know its pretty old thread, but today also requirement is same.
Following is the way I solved this on my Laravel 5.7 (but this will work with any laravel version greater than version 5), I used the knowledge gained from this thread and few other threads to get this working (will leave links to all threads at the end, if this help up-vote those too)
I added this to my helper.php (I used this technique to add helper to my project, but you can use this function directly as well)
if (! function_exists('inline_view')) {
/**
* Get the evaluated view contents for the given blade string.
*
* #param string $view
* #param array $data
* #param array $mergeData
* #return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
function inline_view($view = null, $data = [], $mergeData = [])
{
/* Create a file with name as hash of the passed string */
$filename = hash('sha1', $view);
/* Putting it in storage/framework/views so that these files get cleared on `php artisan view:clear*/
$file_location = storage_path('framework/views/');
$filepath = storage_path('framework/views/'.$filename.'.blade.php');
/* Create file only if it doesn't exist */
if (!file_exists($filepath)) {
file_put_contents($filepath, $view);
}
/* Add storage/framework/views as a location from where view files can be picked, used in make function below */
view()->addLocation($file_location);
/* call the usual view helper to render the blade file created above */
return view($filename, $data, $mergeData);
}
}
Usage is exactly same as laravel's view() helper, only that now first parameter is the blade string
$view_string = '#if(strlen($name_html)>6)
<strong>{{ $name_html }}</strong>
#else
{{$name_html}}
#endif';
return inline_view($view_string)->with('name_html', $user->name);
return inline_view($view_string, ['name_html' => $user->name]);
References:
https://stackoverflow.com/a/31435824/4249775
https://stackoverflow.com/a/33594452/4249775
Laravel 9 :
use Illuminate\Support\Facades\Blade;
return Blade::render('Your Blade Content {{ $parameter1}}', ['parameter1' => 'Name']);

How to use Minify PHP with YUI compressor?

I would like to use YUI compressor with minify PHP rather than the default JSmin. Does anyone have experience setting this up?
Right now I am using the groupsConfig.php to combine the JS.
return array(
'jsAll' => array('//contenido/themes/bam/assets/js/jquery.js', '//contenido/themes/bam/assets/js/modernizr.js','//contenido/themes/bam/assets/js/imgpreload.js', '//contenido/themes/bam/assets/js/imgpreload.js', '//contenido/themes/bam/assets/js/history.js','//contenido/themes/bam/assets/js/ajaxify.js', '//contenido/themes/bam/assets/js/isotope.js'),
'jsHome' => array('//contenido/themes/bam/assets/js/easing.js','//contenido/themes/bam/assets/js/scrollable.js', '//contenido/themes/bam/assets/js/home.js'),
'cssAll' => array('//contenido/themes/bam/bam.css'),
);
As it says on the homepage:
Uses an enhanced port of Douglas Crockford's JSMin library and custom classes to minify CSS and HTML
I have the following code in config.php, but I get a 500 error when trying to view the combined js file:
function yuiJs($js) {
require_once '/lib/Minify/YUICompressor.php';
Minify_YUICompressor::$jarFile = '/lib/yuicompressor-2.4.2.jar';
Minify_YUICompressor::$tempDir = '/temp';
return Minify_YUICompressor::minifyJs($js);
}
$min_serveOptions['minifiers']['application/x-javascript'] = 'yuiJs';
It also appears that there are several lines in lib/Minify/YUICompressor.php that need to be configured, and I'm not sure if I'm doing it right:
class Minify_YUICompressor {
/**
* Filepath of the YUI Compressor jar file. This must be set before
* calling minifyJs() or minifyCss().
*
* #var string
*/
public static $jarFile = '../yuicompressor-2.4.2.jar';
/**
* Writable temp directory. This must be set before calling minifyJs()
* or minifyCss().
*
* #var string
*/
public static $tempDir = '../../temp/';
/**
* Filepath of "java" executable (may be needed if not in shell's PATH)
*
* #var string
*/
public static $javaExecutable = 'java';
I had the same problem on windows. It seems jar file needs to be executable in order to run yui compressor. So, i have to remove excutable check from YUICompressor.php
#132
private static function _prepare()
{
if (! is_file(self::$jarFile)) {
throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
}
// if (! is_executable(self::$jarFile)) {
// throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not executable.');
// }
if (! is_dir(self::$tempDir)) {
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');
}
if (! is_writable(self::$tempDir)) {
throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not writable.');
}
}
and that works fine.

Searching for a better way to set default values in my config class?

I reuse a Config class (simplified code at the bottom) in a lot of my projects. It's generally instantiated once in my app bootstrap file and stored in a container class from which I inject it as a dependency into objects via static "makeThis()" method calls.
Because it's instantiated every time my apps are executed I want to streamline it as much as possible. Repeat: I'm looking to make this process as efficient as possible.
I have default config values specified in the Config::$defaults array at instantiation. What I want to optimize specifically is the assignment of properties inside the load_conf_environment() function. You'll find code comments inside this function further elucidating my requirements.
The code below should be clear enough (to anyone capable of providing a valid answer) to illustrate how the class works. I'm looking for an imaginative and efficient way to handle this operation and I'm not averse to a massive refactoring if you can support your claim.
Finally, let me pre-emptively rule out one suggestion: NO, I'm not interested in using constants for the directory path values. I used to do that but it makes unit testing difficult.
UPDATE:
I've considered using gettype() to determine the default
value's type and subsequently cast the passed value, but according to
the php docs this is not a good solution.
Currently I'm storing the different config directive names in arrays
by type and doing a check to see which array contains the directive
name and perform a typecast or assign values based on that. This seems "ugly" to me
and is what I'm trying to get away from if possible.
UPDATE2:
Having config values match the variable type of the defaults is the ideal situation, but I kind of just got it into my head that I wanted it that way ... it may not even really matter. Maybe it should just be incumbent upon the coder to provide valid configuration values? Anyone have any thoughts on that?
<?php
class Config
{
/**
* The base application directory in which all app_dirs reside
*/
protected $app_path;
/**
* Configuration directives
*
* #var array
* #access protected
*/
protected $vals = array();
/**
* List of default config directive values
*
* #var array
* #access protected
*/
protected $defaults;
/**
* Class constructor -- basically sets default values
*
* #param string $app_path Base application directory
*
* #return void
*/
public function __construct($app_path=NULL)
{
$this->defaults = array(
'debug' => FALSE,
'autoload' => FALSE,
'is_cli_app' => FALSE,
'is_web_app' => FALSE,
'smarty' => FALSE,
'phpar' => FALSE,
'front_url' => '',
'app_dir_bin' => 'bin',
'app_dir_conf' => 'conf',
'app_dir_docs' => 'docs',
'app_dir_controllers' => 'controllers',
'app_dir_lib' => 'lib',
'app_dir_models' => 'models',
'app_dir_test' => 'test',
'app_dir_vendors' => 'vendors',
'app_dir_views' => 'views',
'app_dir_webroot' => 'webroot',
);
if ($app_dir) {
$this->set_app_path($app_path);
}
}
/**
* Setter function for $app_path property
*
* #param string $app_path Path to the app on the server
*
* #return void
* #throws ConfigException On unreadable/nonexistent path
*/
public function set_app_path($app_path)
{
$app_path = dirname(realpath((string)$app_path));
if (is_readable($app_path)) {
$this->app_path = $app_path;
} else {
$msg = 'Specified app path directory could not be read';
throw new ConfigException($msg);
}
}
/**
* Set default values for uninitialized directives
*
* Called after $cfg file is read and applied to fill
* out any unspecified directives.
*
* #return void
*/
protected function set_defaults()
{
foreach ($this->defaults as $key => $val) {
if ( ! isset($this->vals[$key])) {
$this->vals[$key] = $this->is_app_dir($key)
? $this->app_dir . '/' . $this->defaults[$key]
: $this->defaults[$key];
}
}
}
/**
* (Re-)Loads configuration directives
*
* #param mixed $cfg Config array or directory path to config.php
*
* #throws ConfigException On invalid config array
* #return void
*/
public function load_conf_environment($cfg)
{
// Reset all configuration directives
$this->vals = array();
if ( ! is_array($cfg)) {
$cfg = $this->get_cfg_arr_from_file($cfg);
}
if (empty($cfg)) {
$msg = 'A valid $cfg array or environment path is required for ' .
'environment configuration';
throw new ConfigException($msg);
}
foreach ($cfg as $name => $val) {
// does a setter method exist for this directive?
$method = "set_$name";
if (method_exists($this, $method)) {
$this->$method($val);
continue;
}
/*
ASSIGN SPECIFIED DIRECTIVE VALUE
THE ASSIGNED VALUE SHOULD BE OF THE SAME TYPE SPECIFIED by $this->defaults
IF DIRECTIVE IS ONE OF THE 'app_dir' VARS:
- IF IT'S A VALID ABSOLUTE PATH, SET THAT AS THE VALUE
- OTHERWISE, app_dir PATHS SHOULD BE RELATIVE TO $this->app_path
I CONSIDERED USING gettype() TO DETERMINE THE DEFAULT VAR TYPE
AND THEN CAST THE VALUE, BUT THE DOCS SAY THIS IS A BAD IDEA.
*/
}
// set default vals for any directives that weren't specified
$this->set_defaults();
if ( ! $this->vals['is_cli_app'] && ! $this->vals['is_web_app']) {
$msg = 'App must be specified as either WEB or CLI';
throw new Rasmus\ConfigException($msg);
}
}
/**
* Load a configuration array from a specified file
*
* If no valid configuration file can be found,
* an empty array is returned. Valid config paths
*
* #param string $path Config environment directory path
*
* #return array List of config key=>value pairs
* #throws ConfigException On invalid environment path
*/
protected function get_cfg_arr_from_file($path)
{
$path = (string)$path;
$path = rtrim($path, DIRECTORY_SEPARATOR) . '/config.php';
if (is_readable($path)) {
require $path;
if (is_array($cfg) && ! empty($cfg)) {
return $cfg;
}
}
return array();
}
/**
* Has the specified config directive been loaded?
*
* #param string $directive Configuration directive name
*
* #return bool On whether or not a specific directive exists
*/
public function is_loaded($directive)
{
return isset($this->vals[$directive]);
}
/**
* Retrieves a key=>value list of config directives
*
* #return array List of configuration directives
*/
public function get_directives()
{
return $this->vals;
}
/**
* Magic method mapping object properties to $vals array
*
* #param string $name Object property name
*
* #return mixed
*/
public function __get($name)
{
if (isset($this->vals[$name])) {
return $this->vals[$name];
} else {
$msg = "Invalid property: $name is not a valid configuration directive";
throw new OutOfBoundsException($msg);
}
}
}
?>
Handling default values
Is there a reason why you set defaults for directives that need them after loading values from the config file?
Since the defaults are already hard-coded in the __construct, why not just move them to the $vals property and avoid needing to set them later on? This way, your load_conf_environment method overwrites the default value that already exists for the directive.
Validating types
This is something I would enforce with each directive's setter method. Each setter should verify the type of value with the is_* functions or by type-hinting in the setter arguments and throw exceptions accordingly. I would probably go so far as to require each directive have a setter. It's more work, but cleaner and 100% enforceable.
Handling app_dir directives (one option)
In the construct, if set_app_path is called, run through the vals property and call the set_app_dir_directive method (see below) on the app_dir directives so that they are prefixed with the app path.
Make a new set_app_dir_directive method:
public function set_app_dir_directive($key, $val)
{
$this->vals[$key] = $this->app_dir . '/' . $val;
}
// then...
public function load_conf_environment($cfg)
{
...
foreach ($cfg as $name => $val) {
$method = "set_$name";
if (method_exists($this, $method)) {
...
}
if ($this->is_app_dir_directive($name)) {
$this->set_app_dir_directive($name, $val);
continue;
}
// Continue storing vals.
...
}
}

Categories