I´m extending CodeIgniters core lib (Log) with a better threshold level and a way to mail serious errors. But i can´t read my config file from my library for some reason. If i can´t access my config....i can´t get the email addresses to mail to. What am i doing wrong? The message i get is: "Fatal error: Class 'CI_Controller' not found in..."
This is my code so far:
class MY_Log extends CI_Log {
/**
* Constructor
*
* #access public
*/
function MY_Log()
{
parent::__construct();
$this->_levels = array('ERROR' => '1', 'INFO' => '2', 'DEBUG' => '3', 'ALL' => '4');
}
/**
* Write Log File
*
* Calls the native write_log() method and then sends an email if a log message was generated.
*
* #access public
* #param string the error level
* #param string the error message
* #param bool whether the error is a native PHP error
* #return bool
*/
function write_log($level = 'error', $msg, $php_error = FALSE)
{
$result = parent::write_log($level, $msg, $php_error);
$this->ci =& get_instance();
$this->ci->load->config('myconf/mailconf');
echo $this->ci->config->item('emails');
}
}
I think it's because this class (Log/MY_Log) is being loaded too early in the application life cycle and get_instance() isn't available.
Perhaps instead of extending the build in logging functionality you could write your own class to handle such logging (If you do so you could wrap it up and release it as a spark :) )
Alternatively you could use a composer package to provide you with the functionality you are after as I am sure there is one that already exists.
If your not familiar with composer I'd recommend reading Phil Sturgeon's article.
Related
I want to put my module in Prestashop market place, and make it standard everyone can use it. This plugin needs to know the admin directory name dynamically to do its service.
I have searched on the Internet a lot of times, but I didn't find a solution to this issue.
You can use _PS_ADMIN_DIR_ witch is set in [your_admin_dir]/index.php:
if (!defined('_PS_ADMIN_DIR_')) {
define('_PS_ADMIN_DIR_', getcwd());
}
This constant is only set when you're on an admin context. Your FrontOffice doesn't have knowledge of this directory and should not for obvious security reason.
There's also a getAdminLink method in class Link:
/**
* Use controller name to create a link
*
* #param string $controller
* #param bool $with_token include or not the token in the url
* #return string url
*/
public function getAdminLink($controller, $with_token = true)
{
$id_lang = Context::getContext()->language->id;
$params = $with_token ? array('token' => Tools::getAdminTokenLite($controller)) : array();
return Dispatcher::getInstance()->createUrl($controller, $id_lang, $params, false);
}
Example:
// Here we create a link to the dashboard without token
$this->context->link->getAdminLink(Tab::getClassNameById(1), false)
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
I have set of class that send mails to my coworkers, while specific event happens. I had no problem with this so far, until today. I have to run some code from CLI (target - cron) to send daily reports. As you might suspect - no controller nor view is involved.
My code:
/**
* Returns mail content
*
* #param Crm_Mail_Abstract_AlertMail $object
* #return string
* #throws Exception
*/
private function generateContent(Crm_Mail_Abstract_AlertMail $object)
{
$subContent = $object->getInnerContent();
if (!$subContent) {
throw new Exception('Cannot load subcontent');
}
$view = Zend_Layout::getMvcInstance()->getView();
$content = $view->partial($this->mainLayout, array('content' => $subContent));
return $content;
}
And the error I get:
PHP Fatal error: Call to a member function getView() on a non-object
in /home/..../library/Crm/Mail/AlertMail.php on line 195
So... how to render partials while in terminal?
According to This : http://framework.zend.com/manual/1.12/en/zend.layout.quickstart.html
// Returns null if startMvc() has not first been called
$layout = Zend_Layout::getMvcInstance();
So, if you didn't added this :
// In your bootstrap:
Zend_Layout::startMvc();
You will not access this -
Zend_Layout::getMvcInstance()->getView()
So try do this something like that:
Zend_Layout::startMvc();
Zend_Layout::getMvcInstance()->getView();
Is there any way to run a test on output created from a call to 'error_log("Message")' when doing unit tests with phpunit?
Example code, one of my functions tests a credit card with a luhn algorithm:
if($checkLuhn && ($this->_luhn_check($cardNumber) == false)) {
error_log(__METHOD__ . " cardNumber failed luhn algorithm check.");
return false;
}
$checkLuhn is a boolean passed in to tell it whether to do the check, the _luhn_check() returns true if the $cardNumber passes. Problem is, I have more than one test in this function that can return false. I can use assertEquals on the return value, but also want to check why the error was thrown.
Can you override error_log or otherwise grab syslog output in a unit test somehow?
There are a few different ways to direct where error_log() sends data.
First is to just as error_log() to send it some where else. An example would be:
error_log( 'This is a message from error_log()', 3, '/dev/stdout' );
That uses the destination option for error_log().
Another approach is to override the error_log ini setting in PHP. That would look something like:
$cur_error_log = ini_get( 'error_log' );
ini_set( 'error_log', '/dev/stdout' );
error_log( 'This is a message from error_log()' );
ini_set( 'error_log', $cur_error_log );
Of the two options I generally prefer using the destination option in error_log() when possible.
From there you could use expectOutputString() from PHPUnit to look for the data sent from error_log().
I couldn't get this working with error_log, but was able to check the error using trigger_error. Try using annotations.
* #expectedException PHPUnit_Framework_Exception
* #expectedExceptionMessageRegExp /failed/
https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.expectedException
See also :
How to execute code after trigger_error(..., E_USER_WARNING) in unit test (PHPUnit)?
You can use the uopz extension from Zend to overload functions like these.
I use them all the time. Here is an example:
/**
* Requires the following set in php.ini:
* - zend_extension=php_uopz.dll;
* - uopz.overloads=1
*
* #author Aap Noot
*/
class MyClass extends PHPUnit_Framework_TestCase {
public static $error_log = array();
/**
* Overload error_log function
* #see PHPUnit_Framework_TestCase::setUpBeforeClass()
*/
public static function setUpBeforeClass() {
uopz_backup ( "error_log" );
uopz_function ( "error_log", function ($message, $message_type = null, $destination = null, $extra_headers = null) {
// We are only interested in the message
MyClass::$error_log[] = $message;
} );
parent::setUpBeforeClass ();
/**
* Restore function(s)
* #see PHPUnit_Framework_TestCase::tearDownAfterClass()
*/
public static function tearDownAfterClass() {
uopz_restore ( "error_log" );
parent::tearDownAfterClass ();
}
/**
* Set up per test case
* #see PHPUnit_Framework_TestCase::setUp()
*/
protected function setUp() {
parent::setUp ();
MyClass::$error_log = array();
}
/**
* Test error log
* MyClass::$error_log should be an array with the error message
*/
public function testErrorLog() {
// Test response
error_log("This message will be captured");
// Test error log
$this->assertNotEmpty(MyClass::$error_log);
}
}
I have a Logger interface that accepts a SplFileObject in the constructor to use as the file for that particular log. There is also a log($timestamp, $message) method available to actually do the logging. In my first implementation when instantiating a new object and passing a read-only SplFileObject an exception should be thrown. I wrote up an appropriate unit test:
<?php
class FileLoggerTest extends PHPUnit_Framework_TestCase {
/**
* #expectedException \InvalidArgumentException
*/
public function testReadOnlyFileObjectFailure() {
$file = '/Library/WebServer/Documents/sprayfire/tests/mockframework/logs/test-log.txt';
$LogFile = new \SplFileObject($file);
$Logger = new \libs\sprayfire\logger\FileLogger($LogFile);
$Logger->log('test', 'something');
}
}
?>
Normally I would have a method producing the directory name but when I started encountering problems I changed it to an absolute path to rule out that as the cause.
And here's the implementation:
namespace libs\sprayfire\logger;
use \SplFileObject as SplFileObject;
use \InvalidArgumentException as InvalidArgumentException;
use libs\sprayfire\logger\Logger as Logger;
/**
* #brief A framework implemented class that adds a timestamp log message to
* the end of an injected file.
*/
class FileLogger implements Logger {
/**
* #brief A SplFileObject that should be used to write log messages to.
*
* #property $LogFile
*/
protected $LogFile;
/**
* #param $LogFile SplFileObject that should have log messages written to
*/
public function __construct(SplFileObject $LogFile) {
$this->LogFile = $LogFile;
$this->throwExceptionIfFileNotWritable();
}
/**
* #throws InvalidArgumentException
*/
protected function throwExceptionIfFileNotWritable() {
$isWritable = $this->LogFile->isWritable();
if (!$isWritable) {
throw new InvalidArgumentException('The passed file, ' . $this->LogFile->getPathname() . ', is not writable.');
}
}
/**
* #param $timestamp A formatted timestamp string
* #param $message The message string to log
* #return boolean true if the message was logged, false if it wasn't
*/
public function log($timestamp, $message) {
if (!isset($timestamp) || empty($timestamp)) {
$timestamp = 'No timestamp given';
}
if (!isset($message) || empty($message)) {
$message = 'Attempting to log an empty message';
}
$separator = ' := ';
$message = $timestamp . $separator . $message;
$wasWritten = $this->LogFile->fwrite($message);
if (!isset($wasWritten)) {
return false;
}
return true;
}
}
// End FileLogger
The problem is that the test passes and I can tell by the code coverage generated by the test that isWritable() returns true and SplFileObject::fwrite() on a readonly object returns a non-null value as well.
The really, really weird part of this is that the very same code ran in a non-unit test example fails, just as it should.
$logFile = '/Library/WebServer/Documents/sprayfire/tests/mockframework/logs/test-log.txt';
$SplFile = new \SplFileObject($logFile);
$Logger = new \libs\sprayfire\logger\FileLogger($SplFile);
Running this from index.php results in xdebug showing an uncaught InvalidArgumentException from FileLogger with the expected message that the file passed is not writable. This is completely baffling, the same exact code is being ran in both situations, yet the code inside the unit test is "failing" and the non-unit tested code is performing as expected.
Yes, the file exists. SplFileObject would throw an exception if it didn't.
The very exact same code is being ran in both situations, other code that is being ran includes setting up 2 constants, a file directory and a shortcut to DIRECTORY_SEPARATOR, and setting up class autoloading. But, again, this is happening exactly the same in both situations and would result in a failure long before this unit test is actually ran.
Help!
Looking at it now the problem seems relatively simple. PHP is running under the _www user and phpunit is running as the user that installed it. These users have different permissions, which makes perfect sense. If you somehow are encountering this problem I suggest you look at edorian's answer and re-evaluate how you are writing your unit tests.
First off:
For unit testing there is SplTempFileObject extends SplFileObject.
You usually don't need to create real files on the disk for that as it's slow anyways ;)
For an isReadable / isWriteable check in your phpunit tests you generally create ether create non readable / writeable files on the disk or use vfsStreamWrapper where applicable. It also works with SplFileObject.
Our issue:
In the test the last line should be removed. You except the exception during construct so let's make away with that ;)
What I found strange is that you have an absolute path there. Does your root folder structure really start with '/Library/WebServer/Documents/ ? Mainly I'm confused because that would imply that your tests are in a "WebServer" directory. Anyways.. moving on:
"Works for me"
Attached is a standalone version of the test that works and throws the exception like expected.
Apart from telling you that the problem seem to be somewhere else in your setup I don't see much I can do here. Maybe try the InvalidArgumentException without the / or try the test in isolation / with a newly created file.
PHPUnit doesn't interfere with the file handling functions so I'm out of idea apart from that. Does the sample code below work for you? :)
phpunit mep.php
PHPUnit 3.6.5 by Sebastian Bergmann.
.
Time: 1 second, Memory: 5.00Mb
OK (1 test, 1 assertion)
<?php
class FileLoggerTest extends PHPUnit_Framework_TestCase {
/**
* #expectedException InvalidArgumentException
*/
public function testReadOnlyFileObjectFailure() {
$file = __DIR__."/_files/test-log.txt";
touch($file);
chmod($file, 0444);
$LogFile = new \SplFileObject($file);
$Logger = new FileLogger($LogFile);
}
}
class FileLogger {
protected $LogFile;
public function __construct(SplFileObject $LogFile) {
$this->LogFile = $LogFile;
$this->throwExceptionIfFileNotWritable();
}
/**
* #throws InvalidArgumentException
*/
protected function throwExceptionIfFileNotWritable() {
$isWritable = $this->LogFile->isWritable();
if (!$isWritable) {
throw new InvalidArgumentException('The passed file, ' . $this->LogFile->getPathname() . ', is not writable.');
}
}
/**
* #param $timestamp A formatted timestamp string
* #param $message The message string to log
* #return boolean true if the message was logged, false if it wasn't
*/
public function log($timestamp, $message) {
if (!isset($timestamp) || empty($timestamp)) {
$timestamp = 'No timestamp given';
}
if (!isset($message) || empty($message)) {
$message = 'Attempting to log an empty message';
}
$separator = ' := ';
$message = $timestamp . $separator . $message;
$wasWritten = $this->LogFile->fwrite($message);
if (!isset($wasWritten)) {
return false;
}
return true;
}
}