如何使用 PHP 在文件中添加或更新值
How to add or update values in a file using PHP
我正在开发一个小的 class,它将允许我将队列数据写入文件。类似于 PHP $_SESSION.
我认为以下步骤可以解决问题
- 在
'a+'
模式下使用 fopen()
打开文件
- 使用
flock(
) 和 LOCK_EX
类型锁定文件以防止另一个进程使用同一文件
- 使用
fread()
读取文件的现有内容。然后使用 json_decode(array, true)
将数据放入数组
- 现在数据在数组中。如果键存在于数组中更新它的值,否则将键插入数组。
- 在创建一个包含文件中需要的所有数据的数组后,我使用
ftruncate()
截断文件并使用 fwrite()
将新数据写入文件
- 使用
flock(
) 类型 LOCK_UN
解锁文件以允许另一个进程使用它。
- 最后关闭文件
我相信我在 updateCache()
方法中编写了满足上述步骤的代码。但它似乎无法正常工作。它不跟踪数据。
这是我的 class
<?php
namespace ICWS;
use \ICWS\showVar;
use \DirectoryIterator;
/**
* CacheHandler
*
* @package ICWS
*/
class CacheHandler {
/* Max time second allowed to keep a session File */
private $maxTimeAllowed = 300;
private $filename = '';
private $handle;
public function __construct( $filename )
{
$this->filename = $filename;
// 5% chance to collect garbage when the class is initialized.
if(mt_rand(1, 100) <= 5){
$this->collectGarbage();
}
}
private function print_me($a)
{
echo '<pre>';
print_r($a);
echo '</pre>';
}
/**
* Add/Update the cached array in the session
*
* @param string $key
* @param bigint $id
* @param array $field
* @return void
*/
public function updateCache($key, $id, array $field)
{
$currentVal = $field;
$this->openFile();
$this->lockFile();
$storage = $this->readFile();
//create a new if the $id does not exists in the cache
if( isset($storage[$key]) && array_key_exists($id, $storage[$key]) ){
$currentVal = $storage[$key][$id];
foreach($field as $k => $v){
$currentVal[$k] = $v; //update existing key or add a new one
}
}
$storage[$key][$id] = $currentVal;
$this->updateFile($storage);
$this->unlockFile();
$this->closeFile();
}
/**
* gets the current cache/session for a giving $key
*
* @param string $key. If null is passed it will return everything in the cache
* @return object or boolean
*/
public function getCache($key = null)
{
$value = false;
$this->openFile();
rewind($this->handle);
$storage = $this->readFile();
if(!is_null($key) && isset($storage[$key])){
$value = $storage[$key];
}
if(is_null($key)){
$value = $storage;
}
$this->closeFile();
return $value;
}
/**
* removes the $id from the cache/session
*
* @param string $key
* @param bigint $id
* @return boolean
*/
public function removeCache($key, $id)
{
$this->openFile();
$this->lockFile();
$storage = $this->readFile();
if( !isset($storage[$key][$id])){
$this->unlockFile();
$this->closeFile();
return false;
}
unset($storage[$key][$id]);
$this->updateFile($storage);
$this->unlockFile();
$this->closeFile();
return true;
}
/**
* unset a session
*
* @param argument $keys
* @return void
*/
public function truncateCache()
{
if(file_exists($this->filename)){
unlink($this->filename);
}
}
/**
* Open a file in a giving mode
*
* @params string $mode
* @return void
*/
private function openFile( $mode = "a+")
{
$this->handle = fopen($this->filename, $mode);
if(!$this->handle){
throw new exception('The File could not be opened!');
}
}
/**
* Close the file
* @return void
*/
private function closeFile()
{
fclose($this->handle);
$this->handle = null;
}
/**
* Update the file with array data
*
* @param array $data the array in that has the data
* @return void
*/
private function updateFile(array $data = array() )
{
$raw = json_encode($data);
$this->truncateFile();
fwrite($this->handle, $raw);
}
/**
* Delete the file content;
*
* @return void
*/
private function truncateFile( )
{
ftruncate($this->handle, 0);
rewind($this->handle);
}
/**
* Read the data from the opned file
*
* @params string $mode
* @return array of the data found in the file
*/
private function readFile()
{
$length = filesize($this->filename);
if($length > 0){
rewind($this->handle);
$raw = fread($this->handle, $length);
return json_decode($raw, true);
}
return array();
}
/**
* Lock the file
*
* @return void
*/
private function lockFile( $maxAttempts = 100, $lockType = LOCK_EX)
{
$i = 0;
while($i <= $maxAttempts){
// acquire an exclusive lock
if( flock($this->handle, LOCK_EX) ){
break;
}
++$i;
}
}
/**
* Unlock the file
*
* @return void
*/
private function unlockFile()
{
fflush($this->handle);
flock($this->handle, LOCK_UN);
}
/**
* Remove add cache files
*
* @return void
*/
private function collectGarbage()
{
$mydir = dirname($this->filename);
$dir = new DirectoryIterator( $mydir );
$time = strtotime("now");
foreach ($dir as $file) {
if ( !$file->isDot()
&& $file->isFile()
&& ($time - $file->getATime()) > $this->maxTimeAllowed
&& $file->getFilename() != 'index.html'
) {
unlink($file->getPathName());
}
}
}
function addSlashes($str)
{
return addslashes($str);
}
}
我是这样使用的
<?php
require 'autoloader.php';
try {
$cache = new \ICWS\CacheHandler('cache/879');
$field = array('name' => 'Mike A', 'Address' => '123 S Main', 'phone' => '2152456245', 'ext' => 123);
$cache->updateCache('NewKey', 'first', $field);
$cache->updateCache('NewKey', 'second', $field);
$field = array('Address' => '987 S Main', 'phone' => '000000000000', 'ext' => 5555);
$cache->updateCache('NewKey', 'first', $field);
$field = array('locations' => array('S' => 'South', 'N' => 'North', 'E' => 'East'));
$cache->updateCache('NewKey', 'first', $field);
echo '<pre>';
print_r($cache->getCache());
echo '</pre>';
} catch (exception $e){
echo $e->getMessage();
}
?>
我希望数组看起来像这样
Array
(
[first] => stdClass Object
(
[name] => Mike A
[Address] => 987 S Main
[phone] => 000000000000
[ext] => 5555
[locations] => Array
(
[S] => South
[N] => North
[E] => East
)
)
[second] => stdClass Object
(
[name] => Mike A
[Address] => 123 S Main
[phone] => 2152456245
[ext] => 123
)
)
但是第一次执行后,我得到了空白数组。然后在我再次执行脚本后,我得到以下数组。
Array
(
[NewKey] => Array
(
[first] => Array
(
[locations] => Array
(
[S] => South
[N] => North
[E] => East
)
)
)
)
什么可能导致数据无法正确更新?
我使用 $_SESSION
编写了 updateCache()
,这给了我正确的输出,但我需要在没有会话的情况下执行此操作
这是我的会话基础方法
public function updateCache($key, $id, array $field)
{
$currentVal = (object) $field;
//create a new if the $id does not exists in the cache
if( isset($_SESSION[$key]) && array_key_exists($id, $_SESSION[$key]) ){
$currentVal = (object) $_SESSION[$key][$id];
foreach($field as $k => $v){
$currentVal->$k = $v; //update existing key or add a new one
}
}
$_SESSION[$key][$id] = $currentVal;
}
问题出在这里:
private function readFile()
{
$length = filesize($this->filename);
if($length > 0){
rewind($this->handle);
$raw = fread($this->handle, $length);
return json_decode($raw, true);
}
return array();
}
filesize()
的文档指出:
Note: The results of this function are cached. See clearstatcache() for more details.
因为文件在您启动时是空的,所以它会缓存该信息并且您的分支将被完全跳过,直到下一个脚本执行。这应该可以解决它:
private function readFile()
{
rewind($this->handle);
$raw = stream_get_contents($this->handle);
return json_decode($raw, true) ?: [];
}
我正在开发一个小的 class,它将允许我将队列数据写入文件。类似于 PHP $_SESSION.
我认为以下步骤可以解决问题
- 在
'a+'
模式下使用fopen()
打开文件 - 使用
flock(
) 和LOCK_EX
类型锁定文件以防止另一个进程使用同一文件 - 使用
fread()
读取文件的现有内容。然后使用json_decode(array, true)
将数据放入数组
- 现在数据在数组中。如果键存在于数组中更新它的值,否则将键插入数组。
- 在创建一个包含文件中需要的所有数据的数组后,我使用
ftruncate()
截断文件并使用fwrite()
将新数据写入文件
- 使用
flock(
) 类型LOCK_UN
解锁文件以允许另一个进程使用它。 - 最后关闭文件
我相信我在 updateCache()
方法中编写了满足上述步骤的代码。但它似乎无法正常工作。它不跟踪数据。
这是我的 class
<?php
namespace ICWS;
use \ICWS\showVar;
use \DirectoryIterator;
/**
* CacheHandler
*
* @package ICWS
*/
class CacheHandler {
/* Max time second allowed to keep a session File */
private $maxTimeAllowed = 300;
private $filename = '';
private $handle;
public function __construct( $filename )
{
$this->filename = $filename;
// 5% chance to collect garbage when the class is initialized.
if(mt_rand(1, 100) <= 5){
$this->collectGarbage();
}
}
private function print_me($a)
{
echo '<pre>';
print_r($a);
echo '</pre>';
}
/**
* Add/Update the cached array in the session
*
* @param string $key
* @param bigint $id
* @param array $field
* @return void
*/
public function updateCache($key, $id, array $field)
{
$currentVal = $field;
$this->openFile();
$this->lockFile();
$storage = $this->readFile();
//create a new if the $id does not exists in the cache
if( isset($storage[$key]) && array_key_exists($id, $storage[$key]) ){
$currentVal = $storage[$key][$id];
foreach($field as $k => $v){
$currentVal[$k] = $v; //update existing key or add a new one
}
}
$storage[$key][$id] = $currentVal;
$this->updateFile($storage);
$this->unlockFile();
$this->closeFile();
}
/**
* gets the current cache/session for a giving $key
*
* @param string $key. If null is passed it will return everything in the cache
* @return object or boolean
*/
public function getCache($key = null)
{
$value = false;
$this->openFile();
rewind($this->handle);
$storage = $this->readFile();
if(!is_null($key) && isset($storage[$key])){
$value = $storage[$key];
}
if(is_null($key)){
$value = $storage;
}
$this->closeFile();
return $value;
}
/**
* removes the $id from the cache/session
*
* @param string $key
* @param bigint $id
* @return boolean
*/
public function removeCache($key, $id)
{
$this->openFile();
$this->lockFile();
$storage = $this->readFile();
if( !isset($storage[$key][$id])){
$this->unlockFile();
$this->closeFile();
return false;
}
unset($storage[$key][$id]);
$this->updateFile($storage);
$this->unlockFile();
$this->closeFile();
return true;
}
/**
* unset a session
*
* @param argument $keys
* @return void
*/
public function truncateCache()
{
if(file_exists($this->filename)){
unlink($this->filename);
}
}
/**
* Open a file in a giving mode
*
* @params string $mode
* @return void
*/
private function openFile( $mode = "a+")
{
$this->handle = fopen($this->filename, $mode);
if(!$this->handle){
throw new exception('The File could not be opened!');
}
}
/**
* Close the file
* @return void
*/
private function closeFile()
{
fclose($this->handle);
$this->handle = null;
}
/**
* Update the file with array data
*
* @param array $data the array in that has the data
* @return void
*/
private function updateFile(array $data = array() )
{
$raw = json_encode($data);
$this->truncateFile();
fwrite($this->handle, $raw);
}
/**
* Delete the file content;
*
* @return void
*/
private function truncateFile( )
{
ftruncate($this->handle, 0);
rewind($this->handle);
}
/**
* Read the data from the opned file
*
* @params string $mode
* @return array of the data found in the file
*/
private function readFile()
{
$length = filesize($this->filename);
if($length > 0){
rewind($this->handle);
$raw = fread($this->handle, $length);
return json_decode($raw, true);
}
return array();
}
/**
* Lock the file
*
* @return void
*/
private function lockFile( $maxAttempts = 100, $lockType = LOCK_EX)
{
$i = 0;
while($i <= $maxAttempts){
// acquire an exclusive lock
if( flock($this->handle, LOCK_EX) ){
break;
}
++$i;
}
}
/**
* Unlock the file
*
* @return void
*/
private function unlockFile()
{
fflush($this->handle);
flock($this->handle, LOCK_UN);
}
/**
* Remove add cache files
*
* @return void
*/
private function collectGarbage()
{
$mydir = dirname($this->filename);
$dir = new DirectoryIterator( $mydir );
$time = strtotime("now");
foreach ($dir as $file) {
if ( !$file->isDot()
&& $file->isFile()
&& ($time - $file->getATime()) > $this->maxTimeAllowed
&& $file->getFilename() != 'index.html'
) {
unlink($file->getPathName());
}
}
}
function addSlashes($str)
{
return addslashes($str);
}
}
我是这样使用的
<?php
require 'autoloader.php';
try {
$cache = new \ICWS\CacheHandler('cache/879');
$field = array('name' => 'Mike A', 'Address' => '123 S Main', 'phone' => '2152456245', 'ext' => 123);
$cache->updateCache('NewKey', 'first', $field);
$cache->updateCache('NewKey', 'second', $field);
$field = array('Address' => '987 S Main', 'phone' => '000000000000', 'ext' => 5555);
$cache->updateCache('NewKey', 'first', $field);
$field = array('locations' => array('S' => 'South', 'N' => 'North', 'E' => 'East'));
$cache->updateCache('NewKey', 'first', $field);
echo '<pre>';
print_r($cache->getCache());
echo '</pre>';
} catch (exception $e){
echo $e->getMessage();
}
?>
我希望数组看起来像这样
Array
(
[first] => stdClass Object
(
[name] => Mike A
[Address] => 987 S Main
[phone] => 000000000000
[ext] => 5555
[locations] => Array
(
[S] => South
[N] => North
[E] => East
)
)
[second] => stdClass Object
(
[name] => Mike A
[Address] => 123 S Main
[phone] => 2152456245
[ext] => 123
)
)
但是第一次执行后,我得到了空白数组。然后在我再次执行脚本后,我得到以下数组。
Array
(
[NewKey] => Array
(
[first] => Array
(
[locations] => Array
(
[S] => South
[N] => North
[E] => East
)
)
)
)
什么可能导致数据无法正确更新?
我使用 $_SESSION
编写了 updateCache()
,这给了我正确的输出,但我需要在没有会话的情况下执行此操作
这是我的会话基础方法
public function updateCache($key, $id, array $field)
{
$currentVal = (object) $field;
//create a new if the $id does not exists in the cache
if( isset($_SESSION[$key]) && array_key_exists($id, $_SESSION[$key]) ){
$currentVal = (object) $_SESSION[$key][$id];
foreach($field as $k => $v){
$currentVal->$k = $v; //update existing key or add a new one
}
}
$_SESSION[$key][$id] = $currentVal;
}
问题出在这里:
private function readFile()
{
$length = filesize($this->filename);
if($length > 0){
rewind($this->handle);
$raw = fread($this->handle, $length);
return json_decode($raw, true);
}
return array();
}
filesize()
的文档指出:
Note: The results of this function are cached. See clearstatcache() for more details.
因为文件在您启动时是空的,所以它会缓存该信息并且您的分支将被完全跳过,直到下一个脚本执行。这应该可以解决它:
private function readFile()
{
rewind($this->handle);
$raw = stream_get_contents($this->handle);
return json_decode($raw, true) ?: [];
}