I've got a batch, which extends a base class.
I need to implement in base class a unique system execution, made with flock, so everyone extends from base class should have an unique execution
First class
class batch1 extends base_batch
{
public function execute()
{
//do some stuff
}
}
base class
abstract class base_batch
{
private function realPathExecution()
{
$object = new ReflectionObject($this);
$location = $object->getFileName();
return $location;
}
public function ensureOneExecution()
{
$realPath = $this->realPathExecution();
$fp = fopen($realPath, "r+");
if (!flock($fp, LOCK_EX | LOCK_NB, $blocked)) {
if ($blocked) {
// another process holds the lock
print_r("Other sther script in run! \n");
return False;
}
flock($fp, LOCK_UN);
// couldn't lock for another reason, e.g. no such file
print_r("Error! Nothing done. \n");
return False;
}
return true;
}
public function __construct()
{
if (!$this->ensureOneExecution()) {
print_r("Not flock!! \n");
exit(1);
}
//do some stuff
return;
}
....
}
If I use this structure, I can execute 2 or more times simultaneously this code; but if I take outside ensureOneExecution function it works correctly.
Related
Does the flock() function only work if it is used within the same method that the code is executed?
For example, in the following code, the lock is successful:
public function run()
{
$filePointerResource = fopen('/tmp/lock.txt', 'w');
if (flock($filePointerResource, LOCK_EX)) {
sleep(10);
} else {
exit('Could not get lock!');
}
}
However, in the following code, the lock is unsuccessful:
public function run()
{
if ($this->lockFile()) {
sleep(10);
} else {
exit('Could not get lock!');
}
}
private function lockFile()
{
$filePointerResource = fopen('/tmp/lock.txt', 'w');
return flock($filePointerResource, LOCK_EX);
}
I haven't seen any documentation on this, so I am puzzled by this behavior. I am using php version 5.5.35.
I think the issue with your class based attempt is that when the lockFile method finishes the $filePointerResource goes out of scope and that is probably what is releasing the lock
This works which sort of supports that theory
<?php
class test {
public function run()
{
$fp = fopen('lock.txt', 'w');
if ($this->lockFile($fp)) {
echo 'got a lock'.PHP_EOL;
sleep(5);
}
/*
* Not going to do anything as the attempt to lock EX will
* block until a lock can be gained
else {
exit('Could not get lock!'.PHP_EOL);
}
*/
}
private function lockFile($fp)
{
return flock($fp, LOCK_EX);
}
}
$t = new test();
$t->run();
So if you want to lock the file over more than one call to a class method it might be better to keep the filehandle as a class property, then it will remain in scope as long as the class is instantiated and in scope.
<?php
class test {
private $fp;
public function run()
{
$this->fp = fopen('lock.txt', 'w');
if ($this->lockFile()) {
echo 'got a lock'.PHP_EOL;
sleep(5);
}
/*
* Not going to do anything as the attempt to lock EX will
* block until a lock can be gained
else {
exit('Could not get lock!'.PHP_EOL);
}
*/
}
private function lockFile()
{
return flock($this->fp, LOCK_EX);
}
}
$t = new test();
$t->run();
I'm trying to make open source Cache library. The purpose of library is to provide the way of storing the variable (can be object, can be be array, can be anything) to files and then retrieved it back on call. (usually those variable value is result of massive database queries and calculations).
The basic aim of the project is to practice the Object Oriented Design Principle Called Solid.
If any one can indicate where i'm violating solid principle and how to fix it
I totally understand stackoverflow is not a code writing service but hey i'm making this library open source, so it would benefit our community.
So here is my file structure.
I'm new to UML so please ignore if any errors found
here is the classes implementation.
Cache
namespace library\pingle\cache;
use library\pingle\cache\config\CacheConfigurator;
use library\pingle\cache\file\FileHandler;
/**
* #property CacheReader $cache_reader
* #property CacheWriter $cache_write
*/
Class Cache {
private $config;
private $file_hander;
private $cache_reader;
private $cache_write;
private $cache_directory;
private $cache_kept_days;
private $cache_file_prams;
private $function_name;
private $file_path;
function __construct(CacheConfigurator $config) {
$this->file_hander = new FileHandler();
$this->config = $config;
list($this->cache_directory, $this->function_name, $this->cache_kept_days, $this->cache_file_prams) = $this->config->getConfig();
$this->file_path = $this->generateFileName($this->cache_file_prams);
}
public function read() {
if (is_null($this->cache_reader)) {
$this->cache_reader = new CacheReader($this->file_hander);
}
return $this->cache_reader->readCache($this->file_path);
}
public function write($data) {
if (is_null($this->cache_write)) {
$this->cache_write = new CacheWriter($this->file_hander);
}
if (!$this->file_hander->checkDirectory($this->cache_directory . "/" . $this->function_name)) {
$this->file_hander->createDirectory($this->cache_directory . "/" . $this->function_name);
}
$this->cache_write->writeCache($this->file_path, $data);
}
public function check() {
if ($this->file_hander->checkFileExits($this->file_path)) {
if (time() - filemtime($this->file_path) >= 60 * 60 * 24 * $this->cache_kept_days) {
return false;
}
return true;
} else {
return false;
}
}
private function generateFileName(Array $nameprams) {
$this->file_name = "";
$file = "CC";
foreach ($nameprams as $key => $value) {
$file .= "-$key|$value-";
}
$file .= ".bak";
return $this->cache_directory . "/" . $this->function_name . "/" . $file;
}
}
AbstractCache
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
abstract Class AbstractCache {
protected $file_handler;
public function __construct(FileHandler $file_handler) {
$this->file_handler = $file_handler;
}
protected function checkDirectory($path) {
//check directory exists
$dircheck = $this->file_handler->checkDirectory(dirname($path));
if ($dircheck) {
//check directory permission
if ($this->file_handler->checkPermission(dirname($path))) {
return true;
} else {
throw new \Exception("Directory ($path) Permission Error.");
}
} else {
throw new \Exception("Directory ($path) not found.");
}
}
}
CacheReader
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
/**
* #property FileHandler $file_handler
*/
Class CacheReader extends AbstractCache {
public function __construct(FileHandler $file_handler) {
parent::__construct($file_handler);
}
public function readCache($path) {
if ($this->checkDirectory($path)) {
//delete the file if it exits
if ($this->file_handler->checkFileExits($path)) {
return $this->file_handler->readFile($path);
} else {
throw new \Exception("File ($path) not found");
}
}
}
}
CacheWriter
<?php
namespace library\pingle\cache;
use library\pingle\cache\file\FileHandler;
/**
* #property FileHandler $file_handler
*/
Class CacheWriter extends AbstractCache {
public function __construct(FileHandler $file_handler) {
parent::__construct($file_handler);
}
function writeCache($path, $data) {
if ($this->checkDirectory($path)) {
//delete the file if it exits
if ($this->file_handler->checkFileExits($path)) {
$this->file_handler->deleteFile($path);
}
//write cache
$this->file_handler->writeFile($path, $data);
}
}
}
FileHandler
<?php
namespace library\pingle\cache\file;
Class FileHandler {
public function writeFile($path, $data) {
$content = serialize($data);
file_put_contents($path, $content);
}
public function createDirectory($path) {
mkdir($path);
}
public function deleteFile($path) {
unlink($path);
}
public function checkDirectory($path) {
if (file_exists($path)) {
return true;
} else {
return false;
}
}
public function checkPermission($path) {
if (is_writable($path)) {
return true;
} else {
return false;
}
}
public function checkFileExits($path) {
if (is_file($path)) {
return true;
}
return false;
}
public function readFile($path) {
return unserialize(file_get_contents($path));
}
public function checkFileCreated($path, $format = "Y-m-d") {
return date($format, filemtime($path));
}
}
CacheConfigurator
<?php
namespace library\pingle\cache\config;
/**
* #property PramsFormatter $prams_formatter
*/
class CacheConfigurator {
private $prams_formatter;
private $cache_directory;
private $cache_kept_days;
private $cache_file_prams;
private $function_name;
function __construct($file_prams) {
$this->cache_file_prams = $file_prams;
$this->cache_directory = ""; //Def Path
}
public function setCacheDirectory($cache_directory) {
$this->cache_directory = $cache_directory;
return $this;
}
public function setFunction($function) {
$this->function_name = $function;
return $this;
}
public function setCacheKeptDays($cache_kept_days) {
$this->cache_kept_days = $cache_kept_days;
return $this;
}
public function getConfig() {
$this->prams_formatter = new PramsFormatter($this->cache_file_prams);
$this->cache_file_prams = $this->prams_formatter->getFormattedPrams();
$this->function_name = $this->prams_formatter->cleanValue($this->function_name);
return array($this->cache_directory, $this->function_name, $this->cache_kept_days, $this->cache_file_prams);
}
}
PramsFormatter
<?php
namespace library\pingle\cache\config;
class PramsFormatter {
private $cache_file_prams;
public function __construct(Array $prams) {
$this->cache_file_prams = $prams;
$this->formatPrams();
}
public function formatPrams() {
if (is_array($this->cache_file_prams)) {
foreach ($this->cache_file_prams as $k => $value) {
$this->cache_file_prams[$k] = $this->cleanValue($value);
}
}
}
public function cleanValue($value) {
if (is_array($value)) {
throw new \Exception("Array as paramter value is not accepted");
} else {
return str_replace(array(" ", " ", ".", "/", "\\"), "-", $value);
}
}
public function getFormattedPrams() {
return $this->cache_file_prams;
}
}
Usage
$cache_config = new CacheConfigurator(array('carrier_id' => $invoicedata['carrier_id'], 'month' => $month, 'year' => $year));
$cache_config->setFunction('Inter-department Calls');
$cache_config->setCacheKeptDays(30);
$cache_config->setCacheDirectory("bin/cache");
$cache = new Cache($cache_config);
if ($cache->check()) {
$node = $cache->read();
} else {
//calculate node
$cache->write($node);
}
Git Repository with Improve Design
https://github.com/FaizRasool/EPC
Very good question, but an entire book could probably be written on this, which makes it quite hard to answer.
I'd start with this simple question: what better describes caching in the choices below?
Caching is a mechanism that allows to store the result of a function to a file for a number of days in order to provide fast access to it.
Caching is a mechanism that allows to retain the result of an operation for as long as the associated retention policy is satisfied in order to provide fast access to it.
None of the definitions are perfect and that's not the point, but what I wanted to emphasis is that #1 explains caching with very specific infrastructure details while #2 defines the mechanism in a more abstract way.
Hopefully, you will now have realized one of the biggest flaws of your design IMO. The various abstractions are wrong:
The abstraction of the storage mechanism is dependent of a specific infrastructure detail: the entire API revolves around files. What if I wanted an in-memory cache?
The data retention policy algorithm is very specific: data will be retained only for a specific number of days. What if I wanted to express the cache in minutes where the counter resets evertime the data is accessed?
One advice I'd give you is to always challenge your abstractions and make sure that they are not too specific so that your code can be extensible and reusable, but not too wide either. Paying attention to the language of the problem domain greatly helps with that.
There's obviously much more than this that could be said and sometimes being technology-dependent is the right choice, but I think my answer will help...
Can anyone help me with php classes example. I have to make class "information" that has information about users: id, email, password, first name, last name, phone.
Also, class must have a method to print all the user data on the output.
It's really simple skeleton, because you didn't try anything, so just for you to have idea how it works...
class User
{
private $id;
private $email;
// ...
public function __construct($id, $email...)
{
$this->id = $id;
$this->email = $email;
// ...
}
public function printAll()
{
return $this->id . ' ' . $this->email;
}
}
I suggest you reading this: http://codular.com/introducing-php-classes and then come back with any questions.
Here is an example of a PHP class:
class DBIGenerator{
private $table;
private $name;
private $path;
public function __construct($table,$name='default_file.php',
$path='DEFAULTPATH/'){
$this->table=$table;
$this->name=$name;
$this->path=$path;
}
public function generate(){
// build class header
$str='<?php class '.$this->name.'{';
if(!$result=mysql_query('SHOW COLUMNS FROM '.$this->table)){
throw new Exception('Failed to run query');
}
// build data member declaration
if(mysql_num_rows($result)<1){
throw new Exception('Not available columns in table');
}
$methods='';
while($row=mysql_fetch_array($result,MYSQL_ASSOC)){
$str.='private $'.$row['Field'].'=\'\';';
$methods.='public function set'.$row['Field'].'($'.$row
['Field'].'){$this->'.$row['Field'].'=$'.$row
['Field'].';}';
$methods.='public function get'.$row['Field'].'(){return
$this->'.$row['Field'].';}';
// store field names in array
$fields[]=$row['Field'];
}
// build empty constructor
$str.='public function __construct(){}';
// build modifiers and accessors
$str.=$methods;
// build load() method
$str.='public function load(){$r=mysql_query("SELECT * FROM
'.$this->table.' WHERE id=\'$this->id\'");';
$str.='return mysql_fetch_array($r,MYSQL_ASSOC);}';
// build submit() method
$str.='public function submit(){mysql_query("INSERT INTO '.$this-
>table.' SET ';
foreach($fields as $field){
$str.=($field!='id')?$field.'=\'$this->'.$field.'\',':'';
}
$str.='");$this->id=mysql_insert_id();';
$str=preg_replace("/,\"/","\"",$str).'}';
// build update() method
$str.='public function update(){mysql_query("UPDATE '.$this-
>table.' SET ';
foreach($fields as $field){
$str.=($field!='id')?$field.'=\'$this->'.$field.'\',':'';
}
$str=preg_replace("/,$/","",$str);
$str.=' WHERE id=\'$this->id\'");}';
// build delete() method
$str.='public function delete(){mysql_query("DELETE FROM '.
$this->table.' WHERE id=\'$this->id\'");}';
$str.='}?>';
// open or create class file
if(!$fp=fopen($this->path.$this->name.'.php','w')){
throw new Exception('Failed to create class file');
}
// lock class file
if(!flock($fp,LOCK_EX)){
throw new Exception('Unable to lock class file');
}
// write class code to file
if(!fwrite($fp,$str)){
throw new Exception('Error writing to class file');
}
flock($fp,LOCK_UN);
fclose($fp);
// delete temporary variables
unset($fp,$str,$row,$fields,$field,$methods);
}
public function getObject(){
// check if class file exists
if(!file_exists($this->path.$this->name.'.php')){
throw new Exception('Failed to include class file');
}
require_once($this->path.$this->name.'.php');
// create data access object
return new $this->name;
}
}
Read more at http://www.devshed.com/c/a/PHP/Building-ObjectOriented-Database-Interfaces-in-PHP-Updating-the-Application-to-PHP-5/1/#Czocu1kMhhuTvg2e.99
Take a look at the snippet below as a basic way to implementing as expressed:
<?php
class information
{
public $id = 1;
public $email = "mail#mail.com";
public $pw = "A2D7DFEA88AC88"; //Don't forget, PW are usually hashed ;)
public function id() {
echo $this->id;
}
public function email() {
echo $this->email;
}
public function pw() {
echo $this->pw;
}
}
$test = new information();
$test->id;
?>
How to interrupt an execution of a thread from the main context?
In the snippet below - how would one go about stopping the action that the thread does without destroying it?
class ReadFileThread extends Thread
{
public function __construct($file, $chunk = 1024)
{
$this->file = $file;
$this->chunk = $chunk;
}
public function run()
{
if(is_file($this->file) && is_readable($this->file))
{
$fh = fopen($this->file, 'rb');
while(!feof($fh))
{
$content = fread($fh, $this->chunk);
}
fclose($fh);
}
}
}
$num = 10;
$threads = [];
for($i = 0; $i < $num; $i++)
{
$thread = new ReadFileThread('/path/to/10gig_file.txt', 1024);
$threads[] = $thread;
// I start the thread, now it's detached from the main context and is reading the file asynchronously
$thread->start();
}
// The interesting part - I want to random 1 thread whose operation of file reading I want to interrupt
$to_interrupt = $threads[rand(0, $num)];
// How to interrupt the thread without destroying it? I want its context preserved
RandomSeeds answer is close, but open to race conditions.
<?php
class FileReader extends Thread {
public $file;
public $pause;
public function __construct($file) {
$this->file = $file;
$this->pause = false;
}
public function run() {
if (($handle = fopen($this->file, "rb"))) {
$len = 0;
do {
$this->synchronized(function(){
if ($this->paused) {
printf(
"\npausing %lu ...\n", $this->getThreadId());
$this->wait();
}
});
$data = fread($handle, 1024);
$len += strlen($data);
if (($len % 2) == 0) {
printf(
"\r\rread %lu", $len);
}
} while (!feof($handle));
fclose($handle);
}
}
public function pause() {
return $this->synchronized(function(){
return ($this->paused = true);
});
}
public function unpause() {
return $this->synchronized(function(){
$this->paused = false;
if ($this->isWaiting()) {
return $this->notify();
}
});
}
}
function do_something($time) {
$start = time();
while (($now = time()) < ($start + $time)) {
usleep(100);
if (($now % 2) == 0) {
echo ".";
}
}
echo "\n";
}
$reader = new FileReader("/path/to/big/file.ext");
$reader->start();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
?>
It is important that variables used for purposes of synchronization are only ever access in synchronized blocks, I have omitted the implementation of a stop/quit function but it's logic is much the same, as RandomSeeds example shows.
Race conditions lurk within:
public function mine($data) {
/* anyone can set doSynchronization at any time */
if ($this->doSynchronization) {
$this->synchronize(function(){
/* checking the predicate in here is safer */
$this->wait();
});
}
}
Good:
public function mine($data) {
$this->synchronize(function(){
if ($this->doSynchronization) {
$this->wait();
}
});
}
Extreme:
public function mine($data) {
$this->synchronize(function(){
while ($this->doSynchronization) {
$this->wait();
}
});
}
The posix standard would always have you write it the extreme way, I'm not so fussed, whatever works for you. The reason for this extreme code is, allowances must be made for a thread to receive a signal other than the one it is waiting on, many low level signals may result in a thread waking from a call to pthread_cond_wait; checking the predicate in a loop like that guards against what the specification calls spurious wakeups ... but such extreme measures can also lead to ill side effects; the reason those threads receive the low level signal is because it is necessary for them to take some action of some kind, ignoring that can easily cause a different part of the stack to deadlock because it expected your thread to die (or do something else, die is an example) when it was signalled ...
AFAIK you can't arbitrarily pause a concurrent thread, but you can send a notification to it. The other thread must cooperate and willingly pause itself when it receives the notification.
Example:
<?php
class MyThread extends Thread {
private $pauseRequested = false;
private $stopRequested = false;
public function pause() {
$this->synchronized(function($thread){
$thread->pauseRequested = true;
}, $this);
}
public function resume() {
$this->synchronized(function($thread){
$thread->pauseRequested = false;
$thread->notify();
}, $this);
}
public function stop() {
$this->synchronized(function($thread){
$thread->stopRequested = true;
}, $this);
}
public function run() {
echo 'Thread started!' . PHP_EOL;
while (!$this->stopRequested) {
// do the actual work
echo 'Working...';
sleep(1);
// check if we have been requested to pause
$this->synchronized(function($thread){
if ($this->pauseRequested) {
echo 'Paused...';
$thread->wait(); // this is where the magic happens
}
}, $this);
}
if ($this->stopRequested) {
echo PHP_EOL . 'Stopped!' . PHP_EOL;
}
}
}
$t = new MyThread();
$t->start();
sleep(5);
$t->pause();
sleep(2);
$t->resume();
sleep(5);
$t->stop();
// wait for $t to complete
$t->join();
?>
Never used pthreads, tbh, but have you tried making a public boolean flag inside the thread class ?
Ok, messing about with classes in PHP and can't get it to work the way I'm used to as a C++/Java-guy. In the "_init" function, if I run a query at the "// query works here" line", everythong works, but in the "getUserID" function, all that happens is said warning...
"getUserID" gets called from login.php (they are in the same dir):
login.php
<?php
include_once 'sitehandler.php';
include_once 'dbhandler.php';
session_start();
#TODO: Safer input handling
$t_userName = $_POST["name"];
$t_userId = $_SESSION['handler']['db']->getUserID($t_userName);
if ($t_userId != -1) {
$_SESSION['user']['name'] = $t_userName;
$_SESSION['user']['id'] = $t_userId;
}
//error_log("user: " . $_SESSION['user']['name'] . ", id: ". $_SESSION['user']['id']);
header("Location: " . $_SERVER["HTTP_REFERER"]);
?>
dbhandler.php
<?php
include_once 'handler.php';
class DBHandler extends HandlerAbstract {
private $m_handle;
function __construct() {
parent::__construct();
}
public function test() {
#TODO: isdir liquibase
#TODO: isfile liquibase-195/liquibase + .bat + execrights
$this->m_isTested = true;
}
public function _init() {
if (!$this->isTested()) $this->test();
if (!file_exists('files/data.db')) {
#TODO: How to to if host is Windows based?
exec('./files/liquibase-1.9.5/liquibase --driver=org.sqlite.JDBC --changeLogFile=files/data_db.xml --url=jdbc:sqlite:files/data.db update');
#TODO: quit if not success
}
#TODO: Set with default data
try {
$this->m_handle = new SQLite3('files/data.db');
} catch (Exception $e) {
die("<hr />" . $e->getMessage() . "<hr />");
}
// query works here
$this->m_isSetup = true;
}
public function teardown() {
}
public function getUserID($name) {
// PHP Warning: SQLite3::prepare(): The SQLite3 object has not been correctly initialised in
$t_statement = $this->m_handle->prepare("SELECT id FROM users WHERE name = :name");
$t_statement->bindValue(":name", $name, SQLITE3_TEXT);
$t_result = $t_statement->execute();
//var_dump($this->m_handle);
return ($t_result)? (int)$t_result['id']: -1;
}
}
When you place something in the session and the script ends, PHP goes through each object and calls the __sleep() magic function. Once it resumes the session, __wakeup() is called.
Resources are not stored in sessions. So every time you want to have a resource available to you, you must initialize it on every script run. This can easily be done, in your example, by implementing the __wakeup() magic method, which should call $this->_init();.
Your code could then become:
<?php
include_once 'handler.php';
class DBHandler extends HandlerAbstract { private $m_handle;
function __construct() {
parent::__construct();
}
public function test() {
#TODO: isdir liquibase
#TODO: isfile liquibase-195/liquibase + .bat + execrights
$this->m_isTested = true;
}
public function _init() {
if (!$this->isTested()) $this->test();
if (!file_exists('files/data.db')) {
#TODO: How to to if host is Windows based?
exec('./files/liquibase-1.9.5/liquibase --driver=org.sqlite.JDBC --changeLogFile=files/data_db.xml --url=jdbc:sqlite:files/data.db update');
#TODO: quit if not success
}
#TODO: Set with default data
try {
$this->m_handle = new SQLite3('files/data.db');
} catch (Exception $e) {
die("<hr />" . $e->getMessage() . "<hr />");
}
// query works here
$this->m_isSetup = true;
}
public function teardown() {
}
public function getUserID($name) {
// PHP Warning: SQLite3::prepare(): The SQLite3 object has not been correctly initialised in
$t_statement = $this->m_handle->prepare("SELECT id FROM users WHERE name = :name");
$t_statement->bindValue(":name", $name, SQLITE3_TEXT);
$t_result = $t_statement->execute();
//var_dump($this->m_handle);
return ($t_result)? (int)$t_result['id']: -1;
}
/**
* Start up the SQLite resource once
* the session is started.
*
*/
public function __wakeup() {
$this->init();
}
}