File handling functions in PHP class - php

I am trying to debug some scripts that I've done that don't work.
I want to implement the very basic logging (I mean log files) function that I use in the main page script in my class files.
However it doesn't work, for example these simple lines:
if ($file = fopen('C:/wamp/www/xxxx/Logs/General/' . date('Ymd') . '.log', 'a+') {
fputs($file, "[" . date('d/m/Y - H:i:s') . "]\t" . "[" . $type ."]\t" . "[" . $author . "]\t" . $message . "\r\n");
fclose($file);
}
else
{
return false;
}
Work perfectly if I put them in a php function included at the top of my main page (for example in a log.php file).
Howevr they don't work at all if they are in a class method:
public function __contruct(array $connectionArgs)
{
if ($file = fopen('C:/wamp/www/xxxx/Logs/General/' . date('Ymd') . '.log', 'a')) {
fwrite($file, "test");
fclose($file);
}
else
{
die("fail");
}
I am quite new to OOP so I guess it has something to do with the way of calling such function into a class?

It shoudln't be a different if you're putting your logger in class definition or in function code. I assume that you're doing something wrong or maybe you have some error.
Here this is working example
Class Logger
{
const PATH_TO_LOGS_DIRECTORY = 'C:/wamp/www/xxxx/Logs/General/';
const FILE_DATE_SUFFIX = 'Ymd';
private $handle;
public function log($what) {
$this->openFile();
fwrite($this->handle, $what . PHP_EOL);
}
protected function openFile() {
if ($this->handle === null) {
$this->handle = fopen(self::PATH_TO_LOGS_DIRECTORY . date(self::FILE_DATE_SUFFIX) . '.log', 'a');
if ($this->handle === false) {
throw new RuntimeException('Cannot open log file');
}
}
register_shutdown_function(array($this, 'close'));
}
public function close() {
if($this->handle !== null) {
fclose($this->handle);
}
}
}
Few extra things that you should care of :
don't open file till you want to log something. When you're not logging stuff you don't need to reach the file and seek to end of file.. It's called Lazy Initialiation. When you want to log something they you're opening file
in this demo class I'm using a small trick, normally when you're shuttingdown application you should close the log file, (call fclose()), but then you have remember that, and then if you have exception you have to handle that also. But you can use register_shutdown_function and PHP will always call that function on the end of php script
take a look on PSR-3 (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) - PHP group is trying to standarize the logging systems so there is no need to to write your own interface to handle
it's good to pass date string (timestamp or DateTime object event better a param to constructor. You should pass dependency, not expect them

Related

How does a php program decide what function is called first when I go to a php page?

I am trying to understand how a php application that is called with a POST to this URL works:
transliterator/romaji
The romaji.php looks like this:
<?php
namespace JpnForPhp\Transliterator;
class Romaji extends TransliterationSystem
{
private $latinCharacters = array();
public function __construct($system = '')
{
$file = __DIR__ . DIRECTORY_SEPARATOR . 'Romaji' . DIRECTORY_SEPARATOR . (($system) ? $system : 'hepburn') . '.yaml';
parent::__construct($file);
}
public function __toString()
{
return $this->configuration['name']['english'] . ' (' . $this->configuration['name']['japanese'] . ')';
}
TransliterationSystem looks like this:
<?php
namespace JpnForPhp\Transliterator;
use Symfony\Component\Yaml\Yaml;
abstract class TransliterationSystem
{
public $configuration = array();
public function __construct($file)
{
$this->configuration = Yaml::parse(file_get_contents($file));
}
public function transliterate($str)
{
$str = $this->preTransliterate($str);
foreach ($this->configuration['workflow'] as $work) {
if (!method_exists($this, $work['function'])) {
continue;
}
$params = array($str);
if (isset($work['parameters'])) {
$params[] = $work['parameters'];
}
$str = call_user_func_array(array($this, $work['function']), $params);
}
$str = $this->postTransliterate($str);
return $str;
}
Can someone explain to me the sequence of events for when I POST to romaji.php? Below is a link to the github if there is something that I should have included but didn't.
For reference here's the link to github
Normally a PHP file is read (and evaluated) from top to bottom. As pointed out in a comment above, these are just class declarations - there's no code there to actually instantiate the classes or do anything with them, so there's really nothing happening here as such.
For something to happen, there would need to be some code to make use of these classes, for example:
$r = new Romaji();
// Do something with $r ....
EDIT:
I just had a look at the GitHub link, and apparently this is a library; so you'll call it from your own code - it won't do anything by itself.
I'm the one who wrote this library :)
JpnForPhp exposes various helper and functions, that's why you don't see any instanciation like = new Romaji() ; the transliterator component doesn't call himself :)
If you want to see some sample to understand how to use it, please check the test files or the demo website source code
Hope this help.

Passing callback function created in my custom controller

I have the follow code snippet and works well:
<?php
require_once(FOLDER_ROOT . "/lib/transaction/RetornoBanco.php");
require_once(FOLDER_ROOT . "/lib/transaction/RetornoFactory.php");
$fileName = 'test.txt';
function rowProcess($self, $numLn, $vrow) {
print ($numLn . ' - ' . $vrow);
}
$test = RetornoFactory::getRetorno($fileName, 'rowProcess');
$retorno = new RetornoBanco($test);
$retorno->process();
The getRetorno function uses 'rowProcess' as a handler function.
But now I'm trying do it in my custom controller (on my own Magento Extension, looks like Zend Controllers).
For each line of my file (test.txt), rowProcess runs.
But now, in my controller, rowProcess has a class.
My controller:
class MY_CLASS_HERE extends Mage_Adminhtml_Controller_Action
{
public function uploadAction()
{
//just to simplify, this uploaded file exists
$fileName = '/home/user/public_html/app/files/file.RET';
$test = RetornoFactory::getRetorno($fileName, "rowProcess");
$retorno = new RetornoBanco($test);
$retorno->process();
echo 'My Controller Here';
}
public function rowProcess($self, $numLn, $vrow)
{
print ($numLn . ' - ' . $vrow);
//I'm creating log to prevent it is not a problem for standard output
Mage::log($numLn . ' - ' . $vrow);
//This function log is default in Magento and works without problems.
}
}
My controller works well but now the handler doesn't print nothing!
I think it's wrong because now my function handler inside a class and getRetorno function can not use it. What could I do to fix this?
How about using like this? Can you try it?
$test = RetornoFactory::getRetorno($fileName, $this->rowProcess);

MVC and opening file

I have a normal setup for MVC: domain.com/{controller}/{method}/{param}.
I have these 2 functions, the first works and the other one doesn't:
private function _getUserWords()
{
return file(URL . 'public/txt/words.txt');
}
private function _getUserSentences()
{
$file = URL . 'public/txt/' . strtolower($this->view->randomWord) . '.txt';
echo $file;
return file($file);
}
Now the first one actually includes the file, but using the second one, it trys to include the 'public' controller and execute the 'txt' method. I can't seem to figure out why this is, any ideas?

PHP spl_autoload: how to determine the autoload function requestor

Is there a way for the function registered by spl_autoload_register to know the source file/class/method that's calling it? I want to be able to output a useful error when a class is not found so I know which source file needs to be updated. For example:
spl_autoload_register(function($className)
{
$classFile = 'include/' . $className . '.php';
if (!is_readable($classFile))
{
echo 'Could not load ' . $className . ' requested by ' . $source;
// how to figure out $source -----------------------------^^
return false;
}
include $classFile;
return false;
}
That's what a stack trace does. It shows you the chain of events that lead to your error (and can provide details like class, line number, etc)
Try var dumping debug_backtrace() to see the array it returns and if it can helps.
spl_autoload_register(function($className)
{
var_dump(debug_backtrace());
...

PHP Counter Using OOP

I'm new to OOP terminology, I am trying to create a class that make a hit counter.
I try the code below but it create just a counter.txt page with inside value 1. I dont know why its not incrementing.
class LOGFILE {
public function READ($FileName) {
$handle = fopen($FileName, 'r');
$fread = file_get_contents($FileName);
return $fread;
fclose($handle);
}
public function WRITE($FileName, $FileData) {
$handle = fopen($FileName, 'w');
$FileData = $fread +1;
fwrite($handle, $FileData);
fclose($handle);
}
}
$logfile = new LOGFILE();
$logfile -> WRITE("counter.txt",$FileData);
echo $logfile -> READ("counter.txt");
The reason is that $fread is local variable for both READ and WRITE methods. You need to make it private global variable for your class:
class LOGFILE {
private $fread;
public function READ($FileName) {
$this->fread = file_get_contents($FileName);
return $this->fread;
}
public function WRITE($FileName) {
$this->READ($FileName);
$handle = fopen($FileName, 'w');
$FileData = $this->fread +1;
fwrite($handle, $FileData);
fclose($handle);
}
}
$logfile = new LOGFILE();
$logfile -> WRITE("counter.txt");
echo $logfile -> READ("counter.txt");
Note: I have removed fopen and fclose because file_get_contents does not need it. In write you can use file_put_contents. Removed not used variable $FileData too. It's always a good practice to create variables methods and classes when they are needed.
Also take a look at best practices how to name your classes, variables, methods and so on. Here's best guide, IMO.
Let's start going over the corrected code and see what was missing:
<?php
class LOGFILE {
public function READ($FileName) {
$handle = fopen($FileName, 'r');
$fread = fgets($handle, 8192);
fclose($handle);
return $fread;
}
public function WRITE($FileName, $FileData) {
$counter = $this->READ($FileName);
$handle = fopen($FileName, 'w');
fwrite($handle, $FileData + $counter);
fclose($handle);
}
}
$logfile = new LOGFILE();
$FileData = 1;
$logfile -> WRITE("counter.txt",$FileData);
echo $logfile -> READ("counter.txt")."\n";
$logfile -> WRITE("counter.txt",$FileData);
echo $logfile -> READ("counter.txt")."\n";
?>
use of fgets instead of file_get_contents in READ (you can choose to use file_get_contents but I rather stay consistent with the other function that uses fopen)
use of READ inside function WRITE (the principal of code-reuse)
open of file with write permissions in WRITE: 'w'
init $FileData = 1;
no need to hold a private member: $fread
most important: do not write statements after return (like you did in READ) - statements that are written after return will not be executed!
This solution was tested successfully.
OOP must be used where it's needed. You need a simple thing so, no need of OOP.
<?php
function addValue($file='counter.txt', $amount=1) {
if( false == is_file($file) ) {
return false;
}
$initial = file_get_contents($file);
return #file_put_contents($initial+$amount);
}
addValue();
?>
Test your OOP knowledge on something complex, like a shopping cart or some other concept.
EDIT // so, if you need a simple example that looks complex, here you go :)
<?php
class log {
public $file = '';
private $amount = 0;
public function __construct( $file ) {
$this->file = $file;
$this->amount = 1;
}
public function makeAdd() {
$initial = file_get_contents($this->file);
return #file_put_contents($this->file, $initial + $this->amount);
}
function __call($f, $args) {
switch( $f ) {
case 'add':
if(isset($args[0]) && !empty($args[0])) {
$this->amount = (int)$args[0];
}
if( $this->amount == 0 ) {
throw new Exception('Not a valid amount.');
}
return $this->makeAdd();
break;
}
}
}
try {
// create log
$L = new log('count.txt');
// this will add 2
var_dump($L->add(2));
// this will also add 2
var_dump($L->add());
// until you rewrite the amount
var_dump($L->add(1));
// final result -> 5
} catch(Exception $e) {
die($e->getMessage());
}
?>
Good luck!
Use UpperCamelCase for class names. LogFile, not LOGFILE. When you have a variable and the most interesting thing about it is that it's expected to hold a reference to something that is_a LogFile you should name it logFile.
Use lowerCamelCase for functions. read and write, not READ and WRITE
No spaces around the arrow operator
Code after a return statement in a method can never be reached, so delete it.
read() does not use the handle returned by fopen, so don't call fopen
the temp variable $freed doesn't help us understand the code, so we can lose it
read is a slightly unconventional name. If we rename the function to getCount it will be more obvious what it does.
You said you wanted to make a hit counter. So rename the class from LogFile to HitCounter, and the variable to hitCounter
the $FileData parameter to write doesn't get used because the variable is re-assigned inside the function. We can lose it.
The write method is supposed to add one to the number in the file. Write doesn't really express that. Rename it to increment.
Use a blank line between functions. The procedural code at the end should generally be in a separate file, but here we can just add a couple of extra lines. Delete the blanks between the last three lines of code.
Don't repeat yourself - we shouldn't have to mention 'counter.txt' more than once. OOP is all about combining data structures and behaviour into classes, so make a class private variable to hold the filename, and pass it via a constructor
$fread doesn't exist in the scope of increment, so we can't use it. This won't work. Replace it with a call to to getCount()
Swap the first two lines of increment, so we're not doing two concurent accesses to the same file, although we might be running inside a server that's running our script twice and still doing two concurrent accesses.
Rename the variable $FileData to $count, since that's what it is.
Replace the fopen,fwrite,fclose sequence with file_put_contents, since that does the same thing and is more succinct.
We need tag, since our php code continues to the end of the file.
That leaves us with:
<?php
class HitCounter {
private $fileName;
public function __construct($fileName){
$this->fileName = $fileName;
}
public function getCount() {
return file_get_contents($this->fileName);
}
public function increment() {
$count = $this->getCount() + 1;
file_put_contents($this->fileName, $count);
}
}
$hitCounter = new HitCounter("counter.txt");
$hitCounter->increment();
echo $hitCounter->getCount();
You can create a static counter and increment it each time (instead of create file)
<?php
class CountClass {
public static $counter = 0;
function __construct() {
self::$counter++;
}
}
new CountClass();
new CountClass();
echo CountClass::$counter;
?>

Categories