I have problem when i try download many images using Guzzle. Some of them probably invalid, so when i made AsyncRequest i cant handle exception of every images.
I tried to make validation method, which must work with downloaded files, but it doesnt work on-time.
$requests = function () use ($client, $imagesMetaData) {
foreach ($imagesMetaData as $index => $image) {
yield $index => function () use ($client, $image) {
try {
$resource = fopen($image['savePath'], 'w+b');
if (!$resource) {
// do smth
}
$opts = ['sink' => $resource];
$result = $client->requestAsync('GET', $image['downloadUri'], $opts);
return ($result);
} catch (\Exception $e) {
$image['writeLog'](
// do smth
);
return new Response(404);
}
};
}
};
Pool::batch($client, $requests(), [
'concurrency' => 5,
'fulfilled' => function (Response $response, $index) use ($me, $imagesMetaData, &$fulfilled, &$rejected) {
$image = $imagesMetaData[$index];
foreach ($imagesMetaData as $image) {
if ($me->validateDownloadedImage($image)) {
$fulfilled[] = $image;
} else {
$rejected[] = $image;
}
}
},
'rejected' => function ($reason, $index) use ($me, $imagesMetaData, &$rejected) {
$image = $imagesMetaData[$index];
$me->rejectImage($image, $reason);
$rejected[] = $image;
}
]);
return [$fulfilled, $rejected];
If files images are fine - everything work as expected, but if one of them failed - i have next error
Catalog import exception of type RuntimeException thrown in file /var/www/my.site/localhost/core/libraries/guzzlehttp/psr7/src/Stream.php at line 250. Unable to write to stream.
https://i.imgur.com/SBLpni5.png
If someone know this, please help.
I fixed this with opening stream:
$stream = stream_for($resource);
and pass this stream to 'sink'
:
'sink' => $stream
final code looks like this:
$requests = function () use ($client, $imagesMetaData) {
foreach ($imagesMetaData as $index => $image) {
yield $index => function () use ($client, $image) {
try {
$resource = fopen($image['savePath'], 'w+b');
$stream = stream_for($resource);
if (!$resource) {
// do smth
}
$opts = ['sink' => $stream];
$result = $client->requestAsync('GET', $image['downloadUri'], $opts);
return ($result);
} catch (\Exception $e) {
$image['writeLog'](
// do smth
);
return new Response(404);
}
};
}
};
Pool::batch($client, $requests(), [
'concurrency' => 5,
'fulfilled' => function (Response $response, $index) use ($me, $imagesMetaData, &$fulfilled, &$rejected) {
$image = $imagesMetaData[$index];
foreach ($imagesMetaData as $image) {
if ($me->validateDownloadedImage($image)) {
$fulfilled[] = $image;
} else {
$rejected[] = $image;
}
}
},
'rejected' => function ($reason, $index) use ($me, $imagesMetaData, &$rejected) {
$image = $imagesMetaData[$index];
$me->rejectImage($image, $reason);
$rejected[] = $image;
}
]);
return [$fulfilled, $rejected];
Related
Created a simple miniCMS in a portal for content creation. The issue at first was in TinyMCE stripping of id attribute from html tag I've resolved that using valid_elements now the request is being sent to Model as is with no issues however in the Model level it's stripping the id again
Example
<div id="agreement">text ......... </div>
Being Saved in model as
<div>text ......... </div>
The controller code:
public function frontendContent(Request $request, $key)
{
$purifier = new \HTMLPurifier();
$valInputs = $request->except('_token', 'image_input', 'key', 'status', 'type');
foreach ($valInputs as $keyName => $input) {
if (gettype($input) == 'array') {
$inputContentValue[$keyName] = $input;
continue;
}
$inputContentValue[$keyName] = $purifier->purify($input);
}
$type = $request->type;
if (!$type) {
abort(404);
}
$imgJson = #getPageSections()->$key->$type->images;
$validation_rule = [];
$validation_message = [];
foreach ($request->except('_token', 'video') as $input_field => $val) {
if ($input_field == 'has_image' && $imgJson) {
foreach ($imgJson as $imgValKey => $imgJsonVal) {
$validation_rule['image_input.'.$imgValKey] = ['nullable','image','mimes:jpeg,jpg,png,svg'];
$validation_message['image_input.'.$imgValKey.'.image'] = inputTitle($imgValKey).' must be an image';
$validation_message['image_input.'.$imgValKey.'.mimes'] = inputTitle($imgValKey).' file type not supported';
}
continue;
}elseif($input_field == 'seo_image'){
$validation_rule['image_input'] = ['nullable', 'image', new FileTypeValidate(['jpeg', 'jpg', 'png'])];
continue;
}
$validation_rule[$input_field] = 'required';
}
$request->validate($validation_rule, $validation_message, ['image_input' => 'image']);
if ($request->id) {
$content = Frontend::findOrFail($request->id);
} else {
$content = Frontend::where('data_keys', $key . '.' . $request->type)->first();
if (!$content || $request->type == 'element') {
$content = Frontend::create(['data_keys' => $key . '.' . $request->type]);
}
}
if ($type == 'data') {
$inputContentValue['image'] = #$content->data_values->image;
if ($request->hasFile('image_input')) {
try {
$inputContentValue['image'] = uploadImage($request->image_input,imagePath()['seo']['path'], imagePath()['seo']['size'], #$content->data_values->image);
} catch (\Exception $exp) {
$notify[] = ['error', 'Could not upload the Image.'];
return back()->withNotify($notify);
}
}
}else{
if ($imgJson) {
foreach ($imgJson as $imgKey => $imgValue) {
$imgData = #$request->image_input[$imgKey];
if (is_file($imgData)) {
try {
$inputContentValue[$imgKey] = $this->storeImage($imgJson,$type,$key,$imgData,$imgKey,#$content->data_values->$imgKey);
} catch (\Exception $exp) {
$notify[] = ['error', 'Could not upload the Image.'];
return back()->withNotify($notify);
}
} else if (isset($content->data_values->$imgKey)) {
$inputContentValue[$imgKey] = $content->data_values->$imgKey;
}
}
}
}
$content->update(['data_values' => $inputContentValue]);
$notify[] = ['success', 'Content has been updated.'];
return back()->withNotify($notify);
}
When I dd the request
as dd($request) I can see the html tag in full
<div id="agreement">text ......... </div>
But when I dd the content
as dd($content) I can see that the id attribute is stripped
<div>text ......... </div>
The model part
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Frontend extends Model
{
protected $guarded = ['id'];
protected $table = "frontends";
protected $casts = [
'data_values' => 'object'
];
public static function scopeGetContent($data_keys)
{
return Frontend::where('data_keys', $data_keys);
}
}
Kindly asking for help, thank you!
While checking the forum here at SOF I found a solution with a remark from #FarhanIbnWahid thanks to him.
$config = HTMLPurifier_Config::createDefault();
$config->set('Attr.EnableID', true);
$purifier = new \HTMLPurifier($config);
This will resolve the issue.
There are 3 queues in redis.
If I insert a data at a same time with different browsers, both requests are accepted.
I get 2 duplicated records. Any helps?
For example, the first request is on the 1st queue.
At the second request, 2nd queue is searched for finding the data.
But the data doesn't exist in the queue.
The second request is inserted on the queue.
Is it understandable?
public function save($evt_no, $type = "redis") {
$name = '';
$res = true;
try{
$headers = getallheaders();
$bodys = $_REQUEST;
$session = $_SESSION;
foreach ($_FILES as $fileKey=>$fileVal) {
if ($fileVal['error'] == 0) {
try {
$uploaded_row = $this->fileL->upload('queueEventParticipant', $fileKey, array() ,'queue');
} catch (\Exception $ex) {
throw $ex;
}
$bodys['file'][_return_numeric($fileKey)] = $uploaded_row;
}
}
$data = array(
'header' => $headers,
'body' => $bodys,
'session' => $session
);
$data['body']['evt_no'] = $evt_no;
$data = json_encode($data);
if($type == "redis"){
$name = $this->attendQueueRedisService->setNewRsvp($data);
} else {
throw new \Exception("No exist");
}
} catch (\Exception $ex){
log_message("error", $ex->getMessage());
throw $ex;
}
if($res){
return $name;
} else {
return "Error";
}
}
class AttendQueueRedisService extends CI_Model {
private $redisClient;
private $redisConfig;
const PREFIX_DONE = 'done_';
const PREFIX_ERROR = 'error_';
public function __construct() {
parent::__construct();
if ($this->load->config('redis', true, true)) {
$this->redisConfig = $this->config->config['redis'];
}
$this->redisClient = new Redis();
$this->redisClient->connect($this->redisConfig['hostname'], $this->redisConfig['port']);
$this->redisClient->auth($this->redisConfig['auth']);
$this->redisClient->select($this->redisConfig['queue']);
}
public function setNewRsvp($data) {
$key = uniqid('rsvp_'.gethostname().'_',true).':redis';
$this->redisClient->set($key, $data, 60 * 30);
return $key;
}
}
I'm trying to make 4 async search calls (POST HTTP request) to search 4 database tables at the same time, then wait for all of them to finish (asynchronously), combine the results and return them to the user.
Here is my code:
public static function async_search($search_words)
{
$curl = new CurlMultiHandler();
$client = new Client([
'base_uri' => 'https://mytestapi.com/',
'timeout' => 0,
]);
$finished_promisesArr = array(
'search1' => 0,
'search2' => 0,
'search3' => 0,
'search4' => 0,
);
$bodyArr = array(
'search_words' => $search_words,
);
$search1_promise = $client->requestAsync('POST', 'search1', array(
'form_params' => $bodyArr,
))->then(
function(ResponseInterface $response) {
echo 'got response';
$finished_promisesArr['search1'] = 1;
},
function (RequestException $e) {
$finished_promisesArr['search1'] = 1;
}
);
$search2_promise = $client->requestAsync('POST', 'search2', array(
'form_params' => $bodyArr,
))->then(
function(ResponseInterface $response) {
$finished_promisesArr['search2'] = 1;
},
function (RequestException $e) {
$finished_promisesArr['search2'] = 1;
}
);
$search3_promise = $client->requestAsync('POST', 'searchCompanies', array(
'form_params' => $bodyArr,
))->then(
function(ResponseInterface $response) {
$finished_promisesArr['search3'] = 1;
},
function (RequestException $e) {
$finished_promisesArr['search3'] = 1;
}
);
$search4_promise = $client->requestAsync('POST', 'searchCompaniesByIndustries', array(
'form_params' => $bodyArr,
))->then(
function(ResponseInterface $response) {
$finished_promisesArr['search4'] = 1;
},
function (RequestException $e) {
$finished_promisesArr['search4'] = 1;
}
);
$promisesAggregate = GuzzleHttp\Promise\all([$search1_promise, $search2_promise, $search3_promise, $search4_promise]);
foreach ($promisesAggregate as $agg) {
$curl->tick();
}
$keep_running = true;
while ($keep_running) {
$all_processes_finished = true;
foreach ($finished_promisesArr as $promise => $status) {
if (!$status) {
$all_processes_finished = false;
}
}
if ($all_processes_finished) {
$keep_running = false;
}
}
return array();
}
Please ignore the empty array in the result and the fact that I'm not doing anything with the response.
I've got logs on the server methods and they're not even being called, and the loop continues to run infinitely.
Note that when I use the request method instead of requestAsync I do get the correct result.
Any ideas here?
Thank's!
Just call $promisesAggregate->wait() after you get it from GuzzleHttp\Promise\all function.
It means the same as your code, but does it right (your wait code calls ->tick() only once and this is the mistake).
I am trying to get the Image URL of the products by editing the Magento's API items() function. I have used $product-> getImageUrl() to get the URL but I am getting wrong URL.
The URL that I am getting is of the default Image which we place for the products which do not have image(Image Comming Soon like Image's url).
I am calling the function from Android Client using XML-RPC.
I am getting other details of the product correct,but the URL that I am getting for the products is wrong. And, all the URLs of the different products I am getting are same.
FYI, The URL that I am getting in the response is like :
http://192.168.1.237/machinetest/media/catalog/product/cache/0/image/265x/9df78eab33525d08d6e5fb8d27136e95/images/catalog/product/placeholder/image.jpg
The function that I am editing is as followed :
public function items($filters = null, $store = null)
{
$collection = Mage::getModel('catalog/product')->getCollection()
->addStoreFilter($this->_getStoreId($store))
->addAttributeToSelect('name');
if (is_array($filters)) {
try {
foreach ($filters as $field => $value) {
if (isset($this->_filtersMap[$field])) {
$field = $this->_filtersMap[$field];
}
$collection->addFieldToFilter($field, $value);
}
} catch (Mage_Core_Exception $e) {
$this->_fault('filters_invalid', $e->getMessage());
}
}
$result = array();
foreach ($collection as $product) {
//$result[] = $product->getData();
$result[] = array( // Basic product data
'product_id' => $product->getId(),
'sku' => $product->getSku(),
'name' => $product->getName(),
'set' => $product->getAttributeSetId(),
'type' => $product->getTypeId(),
'category_ids'=> $product->getCategoryIds(),
'url_path' => $product-> getImageUrl() // Added the Method here
);
}
return $result;
}
Just write that please try this way..you will get the Solution on the Top
You can able to get Images using this code just go through it and you will get images
public function items($filters = null, $store = null)
{
$collection = Mage::getModel('catalog/product')->getCollection()
->addStoreFilter($this->_getStoreId($store))
->addAttributeToSelect('name');
if (is_array($filters)) {
try {
foreach ($filters as $field => $value) {
if (isset($this->_filtersMap[$field])) {
$field = $this->_filtersMap[$field];
}
$collection->addFieldToFilter($field, $value);
}
} catch (Mage_Core_Exception $e) {
$this->_fault('filters_invalid', $e->getMessage());
}
}
$result = array();
foreach ($collection as $product) {
// $result[] = $product->getData();
$_product = Mage::getModel('catalog/product')->load($product->getId());
$_image=$_product->getImageUrl();
$result[] = array( // Basic product data
'product_id' => $product->getId(),
'sku' => $product->getSku(),
'name' => $product->getName(),
'set' => $product->getAttributeSetId(),
'type' => $product->getTypeId(),
'category_ids'=> $product->getCategoryIds(),
'image_url_path' => $_image
);
}
return $result;
}
hope it's work
if you have any queries tell me i will help you!
I was working with images recently and I believe I have pretty good understanding about it.
I don't think what Josua said is the "real" correct answer. It is good that his answer can solve your problem but I just couldn't stand seeing misleading information.
As for his first option, it is "correct".
Let me break down the code:
Mage_Catalog_Model_Product_Media_Config
public function getMediaUrl($file)
{
$file = $this->_prepareFileForUrl($file);
if(substr($file, 0, 1) == '/') {
return $this->getBaseMediaUrl() . $file;
}
return $this->getBaseMediaUrl() . '/' . $file;
}
public function getBaseMediaUrl()
{
return Mage::getBaseUrl('media') . 'catalog/product';
}
protected function _prepareFileForUrl($file)
{
return str_replace(DS, '/', $file);
}
As you can see, it just simply add media/ + catalog/product + $file.
$file is taken from database, the value will be something like /e/x/example.jpeg
Your uploaded product images are stored inside those folders.
Now, for the problem why $product-> getImageUrl() give you wrong URL is still unknown.
The code that Josua suggest for second option:
$this->helper('catalog/image')
->init($product, $type)
->resize(163, 100);
is "almost" the same with $product->getImageUrl(), it just have difference in resize
Mage_Catalog_Model_Product
public function getImageUrl()
{
return (string)$this->_getImageHelper()->init($this, 'image')->resize(265);
}
So for his second option, it will give the same result with your old code.
I don't know why did he suggest the second option, I think he never check what is behind those functions (not a good idea as it can lead to wrong information)
When you call for $product->getImageUrl(), it will try to load your image from cache if it exists, if not, it will load the image from database (for the path and then will look for your correct image from media folder) and create the cache. If it is unable to find the image or an error occurred, it will get the placeholder image.
My suggestion is to check if there is an exception thrown. You need to use your old code $product->getImageUrl(). Open your app/code/core/Mage/Catalog/Helper/Image.php
Then go to:
Mage_Catalog_Helper_Image
public function __toString()
{
try {
if( $this->getImageFile() ) {
$this->_getModel()->setBaseFile( $this->getImageFile() );
} else {
$this->_getModel()->setBaseFile( $this->getProduct()->getData($this->_getModel()->getDestinationSubdir()) );
}
if( $this->_getModel()->isCached() ) {
return $this->_getModel()->getUrl();
} else {
if( $this->_scheduleRotate ) {
$this->_getModel()->rotate( $this->getAngle() );
}
if ($this->_scheduleResize) {
$this->_getModel()->resize();
}
if( $this->getWatermark() ) {
$this->_getModel()->setWatermark($this->getWatermark());
}
$url = $this->_getModel()->saveFile()->getUrl();
}
} catch( Exception $e ) {
//put log to show error message
Mage::log($e->getMessage());
$url = Mage::getDesign()->getSkinUrl($this->getPlaceholder());
}
return $url;
}
Put Mage::log($e->getMessage()); to log if there is an exception thrown. Most likely your placeholder image is called because there was an exception thrown.
It is just a suggestion from me to ensure there is nothing's wrong with your image / other things as in fact you have solved your problem by directly get the image from media/catalog/product/...
Another correction for Josua's code:
Notice the $full_product = Mage::getModel('catalog/product')->load($product_id);
It is absolutely unnecessary since inside foreach($collection as $product), the product object will be loaded, so another load of product is unnecessary (also $product_id is undefined)
UPDATE, just fixing your code:
public function items($filters = null, $store = null)
{
$collection = Mage::getModel('catalog/product')->getCollection()
->addStoreFilter($this->_getStoreId($store))
->addAttributeToSelect(array('name','image'));
//->addAttributeToSelect('name'); add another select, either image / small_image / thumbnail, modify it as you need
if (is_array($filters)) {
try {
foreach ($filters as $field => $value) {
if (isset($this->_filtersMap[$field])) {
$field = $this->_filtersMap[$field];
}
$collection->addFieldToFilter($field, $value);
}
} catch (Mage_Core_Exception $e) {
$this->_fault('filters_invalid', $e->getMessage());
}
}
$result = array();
foreach ($collection as $product) {
//$result[] = $product->getData();
$result[] = array( // Basic product data
'product_id' => $product->getId(),
'sku' => $product->getSku(),
'name' => $product->getName(),
'set' => $product->getAttributeSetId(),
'type' => $product->getTypeId(),
'category_ids'=> $product->getCategoryIds(),
'url_path' => $product-> getImageUrl() // Added the Method here
);
}
return $result;
}
Your code was awesome!
Yes, you can get image url with :
'url_path' => Mage::getModel('catalog/product_media_config')
->getMediaUrl($product->getImage());//getSmallImage(), getThumbnail()
or another option by calling :
$type = 'small_image';
'url_path' => $this->helper('catalog/image')
->init($product, $type)
->resize(163, 100);
can be changed by 'image' small_image' or 'thumbnail'
Default:
Base Image: 265x265 pixel
Small Image: 135x135 pixel
Thumbnail Image: 75x75 pixel
The easier option (detailed):
public function items($filters = null, $store = null)
{
$collection = Mage::getModel('catalog/product')->getCollection()
->addStoreFilter($this->_getStoreId($store))
->addAttributeToSelect('name');
if (is_array($filters)) {
try {
foreach ($filters as $field => $value) {
if (isset($this->_filtersMap[$field])) {
$field = $this->_filtersMap[$field];
}
$collection->addFieldToFilter($field, $value);
}
} catch (Mage_Core_Exception $e) {
$this->_fault('filters_invalid', $e->getMessage());
}
}
$result = array();
foreach ($collection as $product) {
//$result[] = $product->getData();
$full_product = Mage::getModel('catalog/product')->load($product_id);
$result[] = array( // Basic product data
'product_id' => $product->getId(),
'sku' => $product->getSku(),
'name' => $product->getName(),
'set' => $product->getAttributeSetId(),
'type' => $product->getTypeId(),
'category_ids'=> $product->getCategoryIds(),
'url_path' => $full_product->getImageUrl(),
// 'product_path' => $full_product->getProductUrl()
// you can call $full_product->getData();
);
}
return $result;
}
I have checked the memory whilst sending and receiving data over one connection, and I appear to be correctly clearing variables, as the memory returns to its previous value.
But for some reason if I make a new connection, then close the connection, memory is leaked. I believe the problem may be occurring when a socket is accepted.
I am using PHP 5.2.10
Hopefully one of you can find the time to have a play with the source and figure out where its gone wrong. Thanks in advance
<?php
Class SuperSocket
{
var $listen = array();
var $status_listening = FALSE;
var $sockets = array();
var $event_callbacks = array();
var $recvq = 1;
var $parent;
var $delay = 100; // 10,000th of a second
var $data_buffer = array();
function SuperSocket($listen = array('127.0.0.1:123'))
{
$listen = array_unique($listen);
foreach ($listen as $address)
{
list($address, $port) = explode(":", $address, 2);
$this->listen[] = array("ADDR" => trim($address), "PORT" => trim($port));
};
}
function start()
{
if ($this->status_listening)
{
return FALSE;
};
$this->sockets = array();
$cursocket = 0;
foreach ($this->listen as $listen)
{
if ($listen['ADDR'] == "*")
{
$this->sockets[$cursocket]['socket'] = socket_create_listen($listen['PORT']);
$listen['ADDR'] = FALSE;
}
else
{
$this->sockets[$cursocket]['socket'] = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
};
if ($this->sockets[$cursocket]['socket'] < 0)
{
return FALSE;
};
if (#socket_bind($this->sockets[$cursocket]['socket'], $listen['ADDR'], $listen['PORT']) < 0)
{
return FALSE;
};
if (socket_listen($this->sockets[$cursocket]['socket']) < 0)
{
return FALSE;
};
if (!socket_set_option($this->sockets[$cursocket]['socket'], SOL_SOCKET, SO_REUSEADDR, 1))
{
return FALSE;
};
if (!socket_set_nonblock($this->sockets[$cursocket]['socket']))
{
return FALSE;
};
$this->sockets[$cursocket]['info'] = array("ADDR" => $listen['ADDR'], "PORT" => $listen['PORT']);
$this->sockets[$cursocket]['channels'] = array();
$this->sockets[$cursocket]['id'] = $cursocket;
$cursocket++;
};
$this->status_listening = TRUE;
}
function new_socket_loop(&$socket)
{
$socket =& $this->sockets[$socket['id']];
if ($newchannel = #stream_socket_accept($socket['socket'], 0));//#socket_accept($socket['socket']))
{
socket_set_nonblock($newchannel);
$socket['channels'][]['socket'] = $newchannel;
$channel = array_pop(array_keys($socket['channels']));
$this->remote_address($newchannel, $remote_addr, $remote_port);
$socket['channels'][$channel]['info'] = array('ADDR' => $remote_addr, 'PORT' => $remote_port);
$event = $this->event("NEW_SOCKET_CHANNEL");
if ($event)
$event($socket['id'], $channel, $this);
};
}
function endswith($string, $test) {
$strlen = strlen($string);
$testlen = strlen($test);
if ($testlen > $strlen) return false;
return substr_compare($string, $test, -$testlen) === 0;
}
function recv_socket_loop(&$socket)
{
$socket =& $this->sockets[$socket['id']];
foreach ($socket['channels'] as $channel_id => $channel)
{
unset($buffer);#Flush buffer
$status = #socket_recv($channel['socket'], $buffer, $this->recvq, 0);
if ($status === 0 && $buffer === NULL)
{
$this->close($socket['id'], $channel_id);
}
elseif (!($status === FALSE && $buffer === NULL))
{
$sockid = $socket['id'];
if(!isset($this->data_buffer[$sockid]))
$this->data_buffer[$sockid]='';
if($buffer!="\r"&&$buffer!="\n")
{
//Putty ends with \r\n
$this->data_buffer[$sockid].=$buffer;
}
else if($buffer!="\n") //ignore the additional newline char \n
{
$event = $this->event("DATA_SOCKET_CHANNEL");
if ($event)
$event($socket['id'], $channel_id, $this->data_buffer[$sockid], $this);
unset($this->data_buffer[$sockid]);
}
};
}
}
function stop()
{
$this->closeall();
$this->status_listening = FALSE;
foreach ($this->sockets as $socket_id => $socket)
{
socket_shutdown($socket['socket']);
socket_close($socket['socket']);
};
$event = $this->event("SERVER_STOP");
if ($event)
$event($this);
}
function closeall($socket_id = NULL)
{
if ($socket_id === NULL)
{
foreach ($this->sockets as $socket_id => $socket)
{
foreach ($socket['channels'] as $channel_id => $channel)
{
$this->close($socket_id, $channel_id);
}
}
}
else
{
foreach ($this->sockets[$socket_id]['channels'] as $channel_id => $channel)
{
$this->close($socket_id, $channel_id);
};
};
}
function close($socket_id, $channel_id)
{
unset($this->data_buffer[$socket_id]); //clear the sockets data buffer
$arrOpt = array('l_onoff' => 1, 'l_linger' => 1);
#socket_shutdown($this->sockets[$socket_id]['channels'][$channel_id]['socket']);
#socket_close($this->sockets[$socket_id]['channels'][$channel_id]['socket']);
$event = $this->event("LOST_SOCKET_CHANNEL");
if ($event)
$event($socket_id, $channel_id, $this);
}
function loop()
{
while ($this->status_listening)
{
usleep($this->delay);
foreach ($this->sockets as $socket)
{
$this->new_socket_loop($socket);
$this->recv_socket_loop($socket);
};
$event = $this->event("END_SOCKET_CHANNEL");
if ($event)
$event($this);
};
}
function write($socket_id, $channel_id, $buffer)
{
#socket_write($this->sockets[$socket_id]['channels'][$channel_id]['socket'], $buffer);
#socket_write($this->sockets[$socket_id]['channels'][$channel_id]['socket'], 'Server memory usage: '.memory_get_usage().'/'.memory_get_peak_usage(true)."\r\n");
}
function get_channel_info($socket_id, $channel_id)
{
return $this->sockets[$socket_id]['channels'][$channel_id]['info'];
}
function get_socket_info($socket_id)
{
$socket_info = $this->sockets[$socket_id]['info'];
if (empty($socket_info['ADDR']))
{
$socket_info['ADDR'] = "*";
};
return $socket_info;
}
function get_raw_channel_socket($socket_id, $channel_id)
{
return $this->sockets[$socket_id]['channels'][$channel_id]['socket'];
}
function remote_address($channel_socket, &$ipaddress, &$port)
{
socket_getpeername($channel_socket, $ipaddress, $port);
}
function event($name)
{
if (isset($this->event_callbacks[$name]))
return $this->event_callbacks[$name];
}
function assign_callback($name, $function_name)
{
$this->event_callbacks[$name] = $function_name;
}
};
?>
Server.php
include("supersocket.class.php");
function startswith($string, $test) {
return strpos($string, $test, 0) === 0;
}
function newdata($socket_id, $channel_id, $buffer, &$server)
{
//$server->write($socket_id, $channel_id, ">".$buffer."\r\n");
if($buffer=="STOP")
{
$server->stop();
}
else if($buffer=="DATETIME")
{
$server->write($socket_id, $channel_id, ">".date("dmYHis")."\r\n");
}
else
{
$server->write($socket_id, $channel_id, ">BAD\r\n");
}
};
function newclient($socket_id, $channel_id, &$server)
{
$server->write($socket_id, $channel_id, "HEADER\n\r");
}
$socket = new SuperSocket(array('127.0.0.1:12345'));
$socket->assign_callback("DATA_SOCKET_CHANNEL", "newdata");
$socket->assign_callback("NEW_SOCKET_CHANNEL", "newclient");
$socket->start();
//set_time_limit(60*2);
set_time_limit(60*60*24*5); //5 days
$socket->loop();
Edit: sorry you might need to change the socket accept back to:
if ($newchannel = #socket_accept($socket['socket']))
then close the connection, memory is leaked
This is a tricky one - even the standard reference counting garbage collector only kicks in at intervals which are difficult to predict. Calling gc_collect_cycles() should trigger the gc though. Try calling that whenever you close a connection and see if it makes a difference.
If you're still seeing problems - then check if you've got the cyclic reference counter compiled in - if not, then get it.
The Channel array was never removed upon closing the connection, surprised no one picked up on this. Memory usage is now super tight.
unset($this->sockets[$socket_id]['channels'][$channel_id]);
But it does mean that any event for LOST_SOCKET_CHANNEL is pretty useless for the time being.
Will accept my own answer when stack over flow allows. Thanks for all your help ppl .. i guess..