I'm having a problem with curl_multi_*, I want to create a class / function that receives, lets say 1000 URLs, and processes all those URLs 5 at a time, so when a URL finishes downloading it will allocate the now available slot to a new URL that hasn't been processed yet.
I've seen some implementations of curl_multi, but none of them allows me to do what I want, I believe the solution lies somewhere in the usage of curl_multi_select but the documentation isn't very clear and the user notes don't help much.
Can anyone please provide me with some examples how I can implement such a feature?
Here's one way to do it. This script will fetch any number of urls at a time, and add a new one as each is finished (so it's always fetching $maxConcurrent pages).
$sites = array('http://example.com', 'http://google.com', 'http://stackoverflow.com');
$concurrent = 2; // Any number.
$mc = new MultiCurl($sites, $concurrent);
$mc->process();
echo '</pre>';
class MultiCurl
{
private $allToDo;
private $multiHandle;
private $maxConcurrent = 2;
private $currentIndex = 0;
private $info = array();
private $options = array(CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_TIMEOUT => 3);
public function __construct($todo, $concurrent)
{
$this->allToDo = $todo;
$this->maxConcurrent = $concurrent;
$this->multiHandle = curl_multi_init();
}
public function process()
{
$running = 0;
do {
$this->_addHandles(min(array($this->maxConcurrent - $running, $this->_moreToDo())));
while ($exec = curl_multi_exec($this->multiHandle, $running) === -1) {
}
curl_multi_select($this->multiHandle);
while ($multiInfo = curl_multi_info_read($this->multiHandle, $msgs)) {
$this->_showData($multiInfo);
curl_multi_remove_handle($this->multiHandle, $multiInfo['handle']);
curl_close($multiInfo['handle']);
}
} while ($running || $this->_moreTodo());
return $this;
}
private function _addHandles($num)
{
while ($num-- > 0) {
$handle = curl_init($this->allToDo[$this->currentIndex]);
curl_setopt_array($handle, $this->options);
curl_multi_add_handle($this->multiHandle, $handle);
$this->info[$handle]['url'] = $this->allToDo[$this->currentIndex];
$this->currentIndex++;
}
}
private function _moreToDo()
{
return count($this->allToDo) - $this->currentIndex;
}
private function _showData($multiInfo)
{
$this->info[$multiInfo['handle']]['multi'] = $multiInfo;
$this->info[$multiInfo['handle']]['curl'] = curl_getinfo($multiInfo['handle']);
//print_r($this->info[$multiInfo['handle']]);
$content = curl_multi_getcontent($multiInfo['handle']);
echo $this->info[$multiInfo['handle']]['url'] . ' - ' . strlen($content) . ' bytes<br />';
//echo htmlspecialchars($content);
}
}
Related
i am putting together a basic site with a basic shopping cart, we plan to store sensitive information in the sessions in the future so getting this sorted now is a smart choice.
Everything works fine without the custom session handler, for some context the session handling code is below:
function decrypt($edata, $password) {
$data = base64_decode($edata);
$salt = substr($data, 0, 16);
$ct = substr($data, 16);
$rounds = 3; // depends on key length
$data00 = $password.$salt;
$hash = array();
$hash[0] = hash('sha256', $data00, true);
$result = $hash[0];
for ($i = 1; $i < $rounds; $i++) {
$hash[$i] = hash('sha256', $hash[$i - 1].$data00, true);
$result .= $hash[$i];
}
$key = substr($result, 0, 32);
$iv = substr($result, 32,16);
return openssl_decrypt($ct, 'AES-256-CBC', $key, true, $iv);
}
function encrypt($data, $password) {
// Set a random salt
$salt = openssl_random_pseudo_bytes(16);
$salted = '';
$dx = '';
// Salt the key(32) and iv(16) = 48
while (strlen($salted) < 48) {
$dx = hash('sha256', $dx.$password.$salt, true);
$salted .= $dx;
}
$key = substr($salted, 0, 32);
$iv = substr($salted, 32,16);
$encrypted_data = openssl_encrypt($data, 'AES-256-CBC', $key, true, $iv);
return base64_encode($salt . $encrypted_data);
}
class SecureSessionHandler extends SessionHandler {
protected $name, $cookie;
private $key;
public function __construct($key, $name = 'MY_SESSION', $cookie = [])
{
$this->key = $key;
$this->name = $name;
$this->cookie = $cookie;
$this->cookie += [
'lifetime' => 0,
'path' => ini_get('session.cookie_path'),
'domain' => ini_get('session.cookie_domain'),
'secure' => isset($_SERVER['HTTPS']),
'httponly' => true
];
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-
cbc'));
$this->setup();
}
private function setup()
{
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
session_name($this->name);
session_set_cookie_params(
$this->cookie['lifetime'],
$this->cookie['path'],
$this->cookie['domain'],
$this->cookie['secure'],
$this->cookie['httponly']
);
}
public function start()
{
if (session_id() === '') {
if (session_start()) {
return mt_rand(45, 99) === 55 ? $this->refresh() : true; // 1/5
}
}
return false;
}
public function forget()
{
if (session_id() === '') {
return false;
}
$_SESSION = [];
setcookie(
$this->name,
'',
time() - 42000,
$this->cookie['path'],
$this->cookie['domain'],
$this->cookie['secure'],
$this->cookie['httponly']
);
return session_destroy();
}
public function refresh()
{
return session_regenerate_id(false);
}
public function write($id, $data)
{
$data = encrypt($data, $this->key);
return parent::write($id, $data);
}
public function read($id)
{
$data = parent::read($id);
if (!$data) {
return "";
} else {
return decrypt($data, $this->key);
}
}
public function isExpired($ttl = 30)
{
$last = isset($_SESSION['_last_activity'])
? $_SESSION['_last_activity']
: false;
if ($last !== false && time() - $last > $ttl * 60) {
return true;
}
$_SESSION['_last_activity'] = time();
return false;
}
public function isFingerprint()
{
$hash = md5(
$_SERVER['HTTP_USER_AGENT'] .
(ip2long($_SERVER['REMOTE_ADDR']) & ip2long('255.255.0.0'))
);
if (isset($_SESSION['_fingerprint'])) {
return $_SESSION['_fingerprint'] === $hash;
}
$_SESSION['_fingerprint'] = $hash;
return true;
}
public function isValid()
{
return ! $this->isExpired() && $this->isFingerprint();
}
public function get($name)
{
$parsed = explode('.', $name);
$result = $_SESSION;
while ($parsed) {
$next = array_shift($parsed);
if (isset($result[$next])) {
$result = $result[$next];
} else {
return null;
}
}
return $result;
}
public function put($name, $value)
{
$parsed = explode('.', $name);
$session =& $_SESSION;
while (count($parsed) > 1) {
$next = array_shift($parsed);
if ( ! isset($session[$next]) || ! is_array($session[$next])) {
$session[$next] = [];
}
$session =& $session[$next];
}
$session[array_shift($parsed)] = $value;
}
}
$key = file_get_contents('./key.pem', FILE_USE_INCLUDE_PATH);
$session = new SecureSessionHandler($key);
ini_set('session.save_handler', 'files');
session_set_save_handler($session, true);
session_save_path(__DIR__ . '/sessions');
$session->start('cheese');
if ( ! $session->isValid(5)) {
$session->destroy('cheese');
}
There is the full session handler and encryption/decryption method, everything works fine overall but the problem is when going through my shopping cart code and changing all the $_SESSION variables into the $session->get objects, i managed to replace every $_SESSION then the last one i need to change throws a fatal error:
changing " $_SESSION['cart_contents'] = $this->cart_contents;"
to "$session->get('cart_contents') = $this->cart_contents;"
results in:
Fatal error: Can't use method return value in write context in C:\xampp\htdocs\teck\Cart.php on line 375
The rest of the important code is mentioned below, i made sure nothing was left out, its better to have a long post with all details than getting flamed for a small post with a question missing all details lol.
The page that you can adjust the quantity on is viewcart.php, the code thats important from this page is below:
<input type="number" class="form-control text-center" value="<?php echo
$item["qty"]; ?>" onchange="updateCartItem(this, '<?php echo
$item["rowid"]; ?>')">
and the script on the same page (viewcart.php) that throws the alert is below:
<script>
function updateCartItem(obj,id){
$.get("cartAction.php", {action:"updateCartItem", id:id, qty:obj.value},
function(data){
if(data == 'ok'){
location.reload();
}else{
alert('Cart update failed, please try again.');
}
});
}
</script>
Now finally cartaction.php, the code for the action "updatecartitem" is below:
}elseif($_REQUEST['action'] == 'updateCartItem' && !empty($_REQUEST['id']))
{
$itemData = array(
'rowid' => $_REQUEST['id'],
'qty' => $_REQUEST['qty']
);
$updateItem = $cart->update($itemData);
echo $updateItem?'ok':'err';die;
}
The update function thats in cart.php:
public function update($item = array()){
if (!is_array($item) OR count($item) === 0){
return FALSE;
}else{
if (!isset($item['rowid'], $this->cart_contents[$item['rowid']])){
return FALSE;
}else{
// prep the quantity
if(isset($item['qty'])){
$item['qty'] = filter_var($item['qty'],
FILTER_VALIDATE_INT);
// remove the item from the cart, if quantity is zero
if ($item['qty'] == 0){
unset($this->cart_contents[$item['rowid']]);
return TRUE;
}
}
// find updatable keys
$keys = array_intersect(array_keys($this-
>cart_contents[$item['rowid']]), array_keys($item));
// prep the price
if(isset($item['price'])){
$item['price'] = filter_var($item['price'],
FILTER_VALIDATE_FLOAT);
}
// product id & name shouldn't be changed
foreach(array_diff($keys, array('id', 'name')) as $key){
$this->cart_contents[$item['rowid']][$key] = $item[$key];
}
// save cart data
$this->save_cart();
return TRUE;
}
}
}
And last but not least the save_cart function in cart.php also:
protected function save_cart(){
$this->cart_contents['total_items'] = $this->cart_contents['cart_total']
= 0;
foreach ($this->cart_contents as $key => $val){
// make sure the array contains the proper indexes
if(!is_array($val) OR !isset($val['price'], $val['qty'])){
continue;
}
$this->cart_contents['cart_total'] += ($val['price'] * $val['qty']);
$this->cart_contents['total_items'] += $val['qty'];
$this->cart_contents[$key]['subtotal'] = ($this->cart_contents[$key]
['price'] * $this->cart_contents[$key]['qty']);
}
global $key;
global $session;
// if cart empty, delete it from the session
if(count($this->cart_contents) <= 2){
$session->forget();
return FALSE;
}else{
// original code below
$_SESSION['cart_contents'] = $this->cart_contents;
// code below is what i think will clear everything up but throws the
// error.
$session->get('cart_contents') = $this->cart_contents;
return TRUE;
}
}
The cart code above is in the same file as the session handler,
A bit more of an explanation, with " $_SESSION['cart_contents'] = $this->cart_contents;" (the original code),
i press the button +1 to add extra item into cart, the text field increases 1, and the error alert pops up saying "Cart update failed, please try again", i click ok and the alert goes away and nothing happens, the item qty in the text box stays updated but the actual real cart qty doesnt update until i refresh the page or click a link.
With the standard session handler in place this doesnt happen, everything works perfectly.
Is it a must that i change this $_SESSION['cart_contents'] variable to $session->get('cart_contents')?
I should be able to change that script alert to update the cart properly without throwing the alert but will my custom session handler work flawlessly if i leave that one session variable?
Im very sorry for the long post but i wanted to make sure everything is as clear as possible for other people to also understand if they are having similar problems.
The code does seem quite messy from this point of view but if you were to go over the entire site everything would make much more sense.
I have been on php for about 2 months max now trying my best to pick up as much as possible so please bear with me, obviously most of the code is not as clean as it should be, but this is production.
I have spent the past 3-4 days on this session handler, i really dont want to discard it and just go back to the standard _session.
All & any advice is welcome, constructive criticism will be taken lightly!
I really appreciate the time you have taken to go through this, Thanks for your effort!
You are getting the error at line
$session->get('cart_contents') = $this->cart_contents;
Here I assume the get function returns a reference. So if you want to store $this->cart_contents to the reference variable then you must use $$.
e.g $temp = $session->get('cart_contents');
and use $$temp = $this->cart_contents;
Or if you want to assign $this->cart_contents to the cart values then you must use
$this->cart_contents = $session->get('cart_contents');
You cant just use get method and assign a value to it.
Not much i could really do apart from keep researching, what ive done to get it working is below, but can someone please let me know if this will make my custom sessionhandler not work as intended?
i changed the updateCartItem function so there is no if statement just straight calls the function then reloads the page.
<script>
function updateCartItem(obj,id){
$.get("cartAction.php", {action:"updateCartItem", id:id, qty:obj.value},
function(data){
location.reload();
});
}</script>
This works fine with the save_cart function as below:
protected function save_cart(){
$this->cart_contents['total_items'] = $this->cart_contents['cart_total']
= 0;
foreach ($this->cart_contents as $key => $val){
// make sure the array contains the proper indexes
if(!is_array($val) OR !isset($val['price'], $val['qty'])){
continue;
}
$this->cart_contents['cart_total'] += ($val['price'] * $val['qty']);
$this->cart_contents['total_items'] += $val['qty'];
$this->cart_contents[$key]['subtotal'] = ($this->cart_contents[$key]
['price'] * $this->cart_contents[$key]['qty']);
}
global $key;
global $session;
// if cart empty, delete it from the session
if(count($this->cart_contents) <= 2){
$session->forget();
return FALSE;
}else{
$_SESSION['cart_contents'] = $this->cart_contents;
return TRUE;
}
}
BUT! Shouldnt the $_SESSION variable be replaced with my custom session handler call? ($session->get('cart_contents') = $this->cart_contents;) Obviously il get the error if i change it :(
Everything seems to work fine with the sessions, but will this create a weak point in my session security? And i should be able to use the if statement in the updateCartItem script! There must be a way to use the customsessionhandler call instead of "$_SESSION['cart_contents']".
I have a code that uses pthreads v3.1.6 with PHP 7.0.22. The issue I'm having is that the threads do not return array values. My code is as follows
$threadCount = 1;
$url_idList = range(1,2000);
$url_idListChunked = array_chunk($url_idList, $threadCount);
class WorkerThreads extends Thread {
private $threadName, $url_id;
public function __construct($threadName,$url_id) {
$this->threadName = $threadName;
$this->url_id = $url_id;
$this->result = [];
}
public function run() {
if ($this->threadName && $this->url_id) {
printf('%sLoading URL #: %s' . "\n", $this->threadName, $this->url_id);
$this->result = send_request('GET',$this->url_id,NULL,$this->threadName);
}
}
}
while(count($url_idListChunked)){
$url_idListChunk = array_shift($url_idListChunked);
$workers = [];
foreach (range(0,count($url_idListChunk)-1) as $i) {
$threadName = "Thread #".$i.": ";
$workers[$i] = new WorkerThreads($threadName,$url_idListChunk[$i]);
$workers[$i]->start();
}
foreach (range(0,count($url_idListChunk)-1) as $i) {
$workers[$i]->join();
print_r($workers[$i]);
exit();
echo $workers[$i]['threadName']."Result for URL #: ".$workers[$i]['url_id']."\n";
}
}
function send_request($method,$url_id,$data,$threadName=NULL){
$url = 'https://www.example.tld/?id='.$url_id;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, TRUE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
if(!$data && $method=='POST'){
$data = generate_post_data();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
$response = curl_exec($ch);
while((curl_errno($ch) == 6 OR curl_errno($ch) == 28)){
$response = curl_exec($ch);
echo $threadName.'Curl error #'.curl_errno($ch).' - ' . curl_error($ch)." Retrying.\n";
sleep(2);
}
curl_close($ch);
$result['data'] = $response;
return $result;
}
When I get try to print_t($workers) I get the following error message Uncaught RuntimeException: pthreads detected an attempt to connect to an object which has already been destroyed. Why do I lose the array results? It seems like threads have no problem passing the strings back.
Hie, I am not completely sure using start->join method is the preferred way to go for what you want to achieve? I think you need to use the pool collectable method that is made for in pthreads.
Here is an example that might inspire you in your work if you want to collect results from chunked batches. I personally use it intensively and this is the fastest method to push pthreads into its limits. Careful not to push the pool number over your CPU threads (in this example it s 10 cores).
If I may say regarding your code, never try to output something on screen from a pthreads worker, it will surely mess around. Return the object and echo it on collection. Make sure you have result has public in your class to allow returned object.
Not to mention multi-curl that might be the fastest and appropriate way of doing it.
/* pthreads batches */
$batches = array();
$nbpool = 20; // cpu 10 cores
/* job 1 */
$list = [/* data1 */];
$url_idList[] = array_chunk($list, 5000);
/* job 2 */
$list2 = [/* data2 */];
$url_idList[] = array_chunk($list, 10000);
/* final collected results */
$resultFinal = [];
/* loop across batches */
foreach ($url_idList as $key => $url_idListChunked) {
$url_idListChunk = array_shift($url_idListChunked);
/* for intermediate collection */
$data[$key] = [];
/* how many workers */
$workCount = count($url_idListChunk);
/* set pool job up to max cpu capabilities */
$pool = new Pool($nbpool, Worker::class);
/* pool cycling submit */
foreach (range(1, $workCount) as $i) {
$chunck = $url_idListChunk[$i - 1];
$pool->submit(new WorkerThreads(($i - 1), $chunck));
}
/* on collection cycling */
$collector = function (\Collectable $work) use (&$data) {
/* is worker complete ? */
$isGarbage = $work->isGarbage();
/* worker complete */
if ($isGarbage) {
$result = $work->result;
$info = $work->info;
$data[$key] = $result;
/* echo result info outside worker */
echo($info);
}
return $isGarbage;
};
do {
/* collection on pool stack */
$count = $pool->collect($collector);
$isComplete = count($data) === $workCount;
} while (!$isComplete);
/* push stack results */
array_push($resultFinal, $data);
/* close pool */
$pool->shutdown();
}
class WorkerThreads extends \Threaded implements \Collectable {
private $url_id;
private $isGarbage;
private $threadName;
public $result;
public $info;
public function __construct($i, $url_id) {
$this->threadName = "Thread #" . $i . ": ";
$this->url_id = $url_id;
}
public function run() {
if ($this->threadName && $this->url_id) {
$this->result = send_request('GET', $this->url_id, NULL, $this->threadName);
}
$this->info = $this->threadName . " Result for URL #: " . $this->url_id;
$this->isGarbage = true; // yeah, it s done
}
public function isGarbage(): bool {
return $this->isGarbage;
}
}
I have the simple app below. I'm turning off query logging in Laravel, I'm unsetting where possible, yet this function will only process about 800 records before I'm out of RAM on my 2GB Linode. I know I'm asking a lot of you guys but I can't seem to see where I'm leaking memory.
There are really only two major steps.
Step 1 - Move records from a temp table to production
class ListingMigrator
{
public function __construct($tempListing, $feed)
{
$this->tempListing = $tempListing;
$this->listing = $this->listingInstance();
$this->feed = $feed;
}
public static function migrateListing($listing, $feed)
{
$instance = new static($listing, $feed);
return $instance->migrate();
}
public function migrate()
{
$this->addExternalData();
$this->populateListing();
$this->processPhotos();
$this->deleteTempListing();
}
private function listingInstance()
{
DB::connection()->disableQueryLog();
$listing = Listing::findByMud($this->tempListing->matrix_unique_id);
return $listing ?: new Listing;
}
private function processPhotos()
{
$retsApi = new RetsFeedApi($this->feed);
/* Initialize Object */
$rets = $retsApi->findMostRecent();
$photos = $rets->getPhotosForListing($this->listing->matrix_unique_id);
foreach ($photos as $photo)
{
$uploader = new PhotoProcessor($this->listing, $photo);
$uploader->process();
}
}
private function populateListing()
{
DB::connection()->disableQueryLog();
$this->listing->fill($this->tempListing->toArray());
$this->listing->imported_at = $this->tempListing->created_at;
$this->listing->board = $this->tempListing->board;
return $this->listing->save();
}
private function addExternalData()
{
// Get Google lattitude and longitude
$googlecoords = getGoogleMapInfo($this->tempListing->FullAddress, $this->tempListing->City);
$this->listing->GoogleLat = $googlecoords['GoogleLat'];
$this->listing->GoogleLong = $googlecoords['GoogleLong'];
// Add or update the Subdivision Table (helper function)
$subdivisiondata = SubdivisionUpdate($this->tempListing->board, $this->tempListing->SubCondoName, $this->tempListing->Development);
$this->listing->SubdivisionID = $subdivisiondata['id'];
}
private function deleteTempListing()
{
return $this->tempListing->delete();
}
}
Step 2 - Download photos and reupload to Amazon S3
class PhotoProcessor
{
public function __construct(Listing $listing, $photoData)
{
$this->bucket = 'real-estate-listings';
$this->s3 = App::make('aws')->get('s3');
$this->tempFileName = 'app/storage/processing/images/retsphotoupload';
$this->photoData = $photoData;
$this->listing = $listing;
$this->photo = new RetsPhoto;
}
public function process()
{
$this->storeTempFile();
$this->storeFileInfo();
$this->buildPhoto();
$success = $this->pushToS3();
// if Result has the full URL or you want to build it, add it to $this->photo
DB::connection()->disableQueryLog();
$this->listing->photos()->save($this->photo);
$this->removeTempFile();
unset ($this->photoData);
return $success;
}
private function storeTempFile()
{
return File::put($this->tempFileName, $this->photoData['Data']) > 0;
}
private function storeFileInfo()
{
$fileInfo = getimagesize($this->tempFileName);
// Could even be its own object
$this->fileInfo = [
'width' => $fileInfo[0],
'height' => $fileInfo[1],
'mimetype' => $fileInfo['mime'],
'extension' => $this->getFileExtension($fileInfo['mime'])
];
}
private function buildPhoto()
{
$this->photo->number = $this->photoData['Object-ID']; // Storing this because it is relevant order wise
$this->photo->width = $this->fileInfo['width'];
$this->photo->height = $this->fileInfo['height'];
$this->photo->path = $this->getFilePath();
}
private function getFilePath()
{
$path = [];
if ($this->listing->City == NULL)
{
$path[] = Str::slug('No City');
}
else
{
$path[] = Str::slug($this->listing->City, $separator = '-');
}
if ($this->listing->Development == NULL)
{
$path[] = Str::slug('No Development');
}
else
{
$path[] = Str::slug($this->listing->Development, $separator = '-');
}
if ($this->listing->Subdivision == NULL)
{
$pathp[] = Str::slug('No Subdivision');
}
else
{
$path[] = Str::slug($this->listing->Subdivision, $separator = '-');
}
if ($this->listing->MLSNumber == NULL)
{
$pathp[] = Str::slug('No MLSNumber');
}
else
{
$path[] = Str::slug($this->listing->MLSNumber, $separator = '-');
}
$path[] = $this->photoData['Object-ID'].'.'.$this->fileInfo['extension'];
return strtolower(join('/', $path));
}
private function pushToS3()
{
return $this->s3->putObject([
'Bucket' => $this->bucket,
'Key' => $this->photo->path,
'ContentType'=> $this->fileInfo['mimetype'],
'SourceFile' => $this->tempFileName
]);
}
private function getFileExtension($mime)
{
// Use better algorithm than this
$ext = str_replace('image/', '', $mime);
return $ext == 'jpeg' ? 'jpg' : $ext;
}
private function removeTempFile()
{
return File::delete($this->tempFileName);
}
}
Edit to show RetsPhoto
class RetsPhoto extends Eloquent {
protected $table = 'rets_property_photos';
public function listing() {
return $this->belongsTo('Listing', 'matrix_unique_id', 'matrix_unique_id');
}
}
Edit #2: Chunk Call
This is in the app/command and the only thing in there is the fire() function below:
public function fire()
{
// Turn off query logging
DB::connection()->disableQueryLog();
$feeds = RetsFeed::where('active','=',1)->get();
foreach ($feeds as $feed)
{
$class = "TempListing{$feed->board}";
$listings = $class::orderBy('MatrixModifiedDT','desc');
$listings->chunk(50, function($listings) use($feed) {
$listings->each(function($listing) use ($feed) {
ListingMigrator::migrateListing($listing,$feed);
echo "Feed: $feed->board\r\n";
echo "SubcondoName: $listing->SubCondoName\r\n";
echo "Development: $listing->Development\r\n";
echo "\r\n";
});
});
}
}
I think I have figured it out.
Your system holds in memory all of the photo data. As witnessed by the unset ($this->photoData);
The problem is that you need to first complete the process function. Your application is not likely processing ANY photos so when you keep grabbing them from the file system you run out of memory BEFORE you even process a single one.
To Confirm this, simply grab 1 file not using the chunk method.
I am not very familar with Laravel, it could be grabbing all of the files all at once as well and eating the ram.
You can do some tracing with memory_get_usage(true) to find out exactly where the ram is getting eaten from. I would suggest analysing the fire method first.
I am calling functions with ajax POST and then trying to access objects in class, but for some reason i am getting an NULL after calling an class function
Here is my code
include_once dirname(__FILE__).'/../db/_mysql.php';
include_once dirname(__FILE__).'/../class/door.php';
$db = new _mysql();
if (isset($_POST['door'])) {
if ($_POST['door'] == 'get-default') {
$doors = array();
for ($i = 0; $i < $_POST['amount']; $i++) {
array_push($doors, array(
'door-id' => $i,
'panel-colors' => 'valkoinen'
)
);
}
order_door::setDoors($doors);
}
if ($_POST['door'] == 'get-doors') {
print_r(order_door::getDoors());
$doors = order_door::getDoors();
if ((int) $_POST['total-count'] > count($doors)) {
echo $_POST['total-count'] . '>' . count($doors);
} else if ((int) $_POST['total-count'] < count($doors)) {
echo $_POST['total-count'] . '<' . count($doors);
}
}
}
class order_door {
private static $doors;
public static function getDoors() {
return self::$doors;
}
public static function setDoors($array) {
if (count($array) == 0) {
self::$doors = array();
} else {
self::$doors = $array;
print_r(self::getDoors());
}
}
}
in second ajax call i am trying to access to get-doors
in POST get-doors print_r(order_door::getDoors()); is not printing anything. Any ideas?
Actually this is what must happen , you didn't have any data in
private static $doors;
So you set it in one ajax request and call it in another will reset it to default.
To fix this behavior you have to save this data to SESSION for example .
It seems I've hit a wall here and could use some help
Would like to pass a MySql variable to a Joomla Module
Im using Yootheme's Widget-kit to display tweets from a search term. Works great and all but you need to enter the twitter search term in the Module back end.
Instead I would like to use a variable ( already used on the page) and pass that variable to the Twitter module so it can display the tweets I want
Here are some lines of PHP with the variable I'd like to use
$document->setTitle(JText::sprintf('COVERAGE_DATA_PLACE', $this->country->country_name, $this->city->city_name));
$text = JString::str_ireplace('%city_name%',$this->city->city_name,$text);
$this->setBreadcrumbs(array('country','city'));
Is there any way to take the "City" variable and send it to the 'word" field found in the twitter module?
Here is the Code for the twitter module
<?php
Class: TwitterWidgetkitHelper
Twitter helper class
*/
class TwitterWidgetkitHelper extends WidgetkitHelper {
/* type */
public $type;
/* options */
public $options;
/*
Function: Constructor
Class Constructor.
*/
public function __construct($widgetkit) {
parent::__construct($widgetkit);
// init vars
$this->type = strtolower(str_replace('WidgetkitHelper', '', get_class($this)));
$this->options = $this['system']->options;
// create cache
$cache = $this['path']->path('cache:');
if ($cache && !file_exists($cache.'/twitter')) {
mkdir($cache.'/twitter', 0777, true);
}
// register path
$this['path']->register(dirname(__FILE__), $this->type);
}
/*
Function: site
Site init actions
Returns:
Void
*/
public function site() {
// add translations
foreach (array('LESS_THAN_A_MINUTE_AGO', 'ABOUT_A_MINUTE_AGO', 'X_MINUTES_AGO', 'ABOUT_AN_HOUR_AGO', 'X_HOURS_AGO', 'ONE_DAY_AGO', 'X_DAYS_AGO') as $key) {
$translations[$key] = $this['system']->__($key);
}
// add stylesheets/javascripts
$this['asset']->addFile('css', 'twitter:styles/style.css');
$this['asset']->addFile('js', 'twitter:twitter.js');
$this['asset']->addString('js', sprintf('jQuery.trans.addDic(%s);', json_encode($translations)));
// rtl
if ($this['system']->options->get('direction') == 'rtl') {
$this['asset']->addFile('css', 'twitter:styles/rtl.css');
}
}
/*
Function: render
Render widget on site
Returns:
String
*/
public function render($options) {
if ($tweets = $this->_getTweets($options)) {
// get options
extract($options);
return $this['template']->render("twitter:styles/$style/template", compact('tweets', 'show_image', 'show_author', 'show_date', 'image_size'));
}
return 'No tweets found.';
}
/*
Function: _getURL
Create Twitter Query URL
Returns:
String
*/
protected function _getURL($options) {
// get options
extract($options);
// clean options
foreach (array('from_user', 'to_user', 'ref_user', 'word', 'nots', 'hashtag') as $var) {
$$var = preg_replace('/[##]/', '', preg_replace('/\s+/', ' ', trim($$var)));
}
// build query
$query = array();
if ($from_user) {
$query[] = 'from:'.str_replace(' ', ' OR from:', $from_user);
}
if ($to_user) {
$query[] = 'to:'.str_replace(' ', ' OR to:', $to_user);
}
if ($ref_user) {
$query[] = '#'.str_replace(' ', ' #', $ref_user);
}
if ($word) {
$query[] = $word;
}
if ($nots) {
$query[] = '-'.str_replace(' ', ' -', $nots);
}
if ($hashtag) {
$query[] = '#'.str_replace(' ', ' #', $hashtag);
}
$limit = min($limit ? intval($limit) : 5, 100);
// build timeline url
if ($from_user && !strpos($from_user, ' ') && count($query) == 1) {
$url = 'http://twitter.com/statuses/user_timeline/'.strtolower($from_user).'.json';
if ($limit > 15) {
$url .= '?count='.$limit;
}
return $url;
}
// build search url
if (count($query)) {
$url = 'http://search.twitter.com/search.json?q='.urlencode(implode(' ', $query));
if ($limit > 15) {
$url .= '&rpp='.$limit;
}
return $url;
}
return null;
}
/*
Function: _getTweets
Get Tweet Object Array
Returns:
Array
*/
protected function _getTweets($options) {
// init vars
$tweets = array();
// query twitter
if ($url = $this->_getURL($options)) {
if ($path = $this['path']->path('cache:twitter')) {
$file = rtrim($path, '/').sprintf('/twitter-%s.php', md5($url));
// is cached ?
if (file_exists($file)) {
$response = file_get_contents($file);
}
// refresh cache ?
if (!file_exists($file) || (time() - filemtime($file)) > 300) {
// send query
$request = $this['http']->get($url);
if (isset($request['status']['code']) && $request['status']['code'] == 200) {
$response = $request['body'];
file_put_contents($file, $response);
}
}
}
}
// create tweets
if (isset($response)) {
$response = json_decode($response, true);
if (is_array($response)) {
if (isset($response['results'])) {
foreach ($response['results'] as $res) {
$tweet = new WidgetkitTweet();
$tweet->user = $res['from_user'];
$tweet->name = $res['from_user'];
$tweet->image = $res['profile_image_url'];
$tweet->text = $res['text'];
$tweet->created_at = $res['created_at'];
$tweets[] = $tweet;
}
} else {
foreach ($response as $res) {
$tweet = new WidgetkitTweet();
$tweet->user = $res['user']['screen_name'];
$tweet->name = $res['user']['name'];
$tweet->image = $res['user']['profile_image_url'];
$tweet->text = $res['text'];
$tweet->created_at = $res['created_at'];
$tweets[] = $tweet;
}
}
}
}
return array_slice($tweets, 0, $options['limit'] ? intval($options['limit']) : 5);
}
}
class WidgetkitTweet {
public $user;
public $name;
public $image;
public $text;
public $created_at;
public function getLink() {
return 'http://twitter.com/'.$this->user;
}
public function getText() {
// format text
$text = preg_replace('#(https?://([-\w\.]+)+(/([\w/_\.]*(\?\S+)?(#\S+)?)?)?)#', '$1', $this->text);
$text = preg_replace('/#(\w+)/', '#$1', $text);
$text = preg_replace('/\s+#(\w+)/', ' #$1', $text);
return $text;
}
}
// bind events
$widgetkit = Widgetkit::getInstance();
$widgetkit['event']->bind('site', array($widgetkit['twitter'], 'site'));
I believe you can do what you want with this:
http://www.j-plant.com/joomla-extensions/free-extensions/26-module-plant.html
This plugin supports module parameters overriding. Use the next code
for overriding parameters:
[moduleplant id="77" <param_name_1>="<param_value_1>" <param_name_N>="<param_value_N>"]
replace and with the necessary parameter name and value.
You can override as many parameters as you want.
Available module parameters can be found in module XML manifest
file. A manifest file is usually located in the next path:
modules/<module_type>/<module_type>.xml