Jump to content

NotionCommotion

Members
  • Posts

    2,446
  • Joined

  • Last visited

  • Days Won

    10

Everything posted by NotionCommotion

  1. Actually, worked better than I expected. Still would appreciate any feedback. Thanks $time=microtime(true); logger('start'); $loop = \React\EventLoop\Factory::create(); $launcher = new ProcessLauncher($loop); $shell = $launcher->createDeferredShell('bash'); $promises = []; $shell = $launcher->createDeferredShell('bash'); logger('create bash promises'); foreach($cmds as $name=>$cmd) { $promises[] = $shell->execute($cmd)->then( function ($value) use($name) { logger($name.' image complete. Do anything here? $value is the output of the script'); //throw new \Exception('error in items promise'); }, function (\Throwable $e) { logger('image error handler. What should be done here?: '.$e->getMessage()); } ); } logger('bash promises complete'); $allImagePromises = \React\Promise\all($promises); logger('all promise complete'); $filenames = array_keys($cmds); $allImagePromises->then( function($value) use($filenames){ logger('then all image promises are resolved so deal with emails. Do anything with $value which is [0=>null, 1=>null, 2=>null]'); //throw new \Exception('error in email promise'); $files = []; foreach($filenames as $file) { $files[] = sprintf('name: %s type: %s size: %d', $file, filetype($file), filesize($file)); } logger(implode(', ',$files)); // Instead of using futureTick, maybe just add emails as promises? }, function (\Throwable $e) { logger('images error handler. What should be done here?: '.$e->getMessage()); } )->done( function($value){ logger('done. what should be performed here?'); } ); // Should I be using Deferred::resolve()??? logger('before shell end'); $shell->end(); logger('after shell end'); $loop->run();
  2. I was trying to prevent bottlenecks when sending emails, and the below post was the solution. Similar predicament now, but I also need to generate some images first so they may be attached to the emails and the images are generated by a fairly slow command line program that emulates a browser. Multiple emails might or might need the same image so I plan on creating all the images before queuing the emails. I believe the futureTick approach executes each tick in series as blocking, but doesn't attempt to execute all of them at once so it doesn't impact the rest of the application. Promises, however, appear to be more about a structured process for managing callback functions. As such, the futureTick approach would likely be best for tasks requiring much PHP processing, and promises are more suitable for tasks which PHP is just waiting for a response from some other service. True? Would one ever want to attempt to use both approaches? Seems like it might make sense if the code within a promise was PHP intensive to break it down into multiple futureTick tasks, but not not feed promises into the futureTick as promises are "kind of" non-blocking, true? To execute the CLI image program, considering https://github.com/clue/reactphp-shell. Or do you think plan old exec() would be applicable? For using promises, would it just look something like the following? $deferred = new React\Promise\Deferred(); $deferred->promise() ->then(function ($rs) { // create the first image })->then(function ($rs) { // create the second image })->then(function ($rs) { // create the last image })->then(function ($rs) { // send the first email })->then(function ($rs) { // send the second email })->then(function ($rs) { // send the last email }); $deferred->resolve($nothing?); Or maybe something like the following? I would be amazed if it works as is, but hopefully is on the right track. $deferred = new React\Promise\Deferred(); $deferred->promise() ->then(function ($rs) { // Create images // See https://github.com/clue/reactphp-shell $launcher = new ProcessLauncher($loop); $shell = $launcher->createDeferredShell('bash'); $shell ->execute('create the first image') ->execute('create the second image') ->execute('create the lst image') ->then(function ($rs) use($loop) { // Send emails // Just showing this way but will actually use kicken's EmailQueue solution $loop->futureTick(function () { // send the first email }); $loop->futureTick(function () { // send the second email }); $loop->futureTick(function () { // send the last email }); }); }); $deferred->resolve($someQueueOfImages?);
  3. I never understood the use for the default microtime() and even less understood why it is the default, and always pass the true argument to it. $timeFloat = microtime(true); $timeString = microtime(); var_dump($timeFloat); var_dump($timeString); $p=explode(' ', $timeString); var_dump($p[1]+$p[0]);
  4. As a protocol goes, Modbus is very simple, ascii, really old, and well documented. I've never done anything with Modbus/TCP but did quite a bit with Modbus/RTU (serial) years ago. Pretty much just an address, write/read command, and data with some meta start and end bytes. It is my understanding that Modbus/TCP is pretty much the same thing but wrapped in some sort of IP packet. You will either want to be a master/client or slave/server (master/slave for serial and client/server for IP). For a master/client, you put some bytes on the wire and the device you address either writes that data to itself or returns some data (been about 20 years and forgot the details). Slave/servers listen for the start bytes and then capture some amount of bytes. Pay attention to the meta start and checksum bytes. Assuming you are going IP and want to start from scratch, I would first create either a socket server or client (see https://reactphp.org/socket/#quickstart-example). You will also want to use something like wireshark to better understand the data. Alternatively, you can just search "modbus php" and might find something a little more baked as a starting point.
  5. Post the code you are trying. Also, when doing so, but the code tags (i.e. <>) around them do it appears correct. Thanks
  6. I think I have answers to these two items. Monitor when users log on and make changes and update the API as appropriate. Make sense? What should be done if a non-logged on user first accesses a public route, gets a JWT, and stores it in a session, but then later logs on and accesses a private route? The webserver thinks it has a valid JWT and will send it but the REST API will then decrypt it and find there is no user it. One option is for the webserver to use two sessions, but this sounds kludgy. Or maybe the REST API returns some header which instructs the webserver to re-authenticate, but not sure if even an option, and if so how to cleanly prevent some loop. Also, would it be necessary to issue a new JWT or can the payload in a JWT be changed? Any ideas how to deal with using the user's password on the CRM to also authenticate on the REST API? The GUID is probably secret enough for the application and if an issue, can just use the GUID and username. Still would appreciate advice on the following (as well as you feel my above comment is wrong). Is GET appropriate for requesting the JWT's or should I use some other method? Is it appropriate to include the user's access level in the JWT payload? Will one need to wait until the JWT has expired before their access level changes? Am I going down an reasonable path and anything else obvious I should be considering?
  7. I am currently doing the following but wish to change to using JWTs. A webserver is running some CRM system which has its own authentication system and browsers can access public routes without logging and but must log on first to access private routes. All the routes on the webserver which are prefixed by "api" will be forwarded to specific REST API along with an "account" GUID in the header and the user's ID if it exists. For the routes that require a user to be logged in, the webserver will first check if a session exists, and if not make a preliminary GET request to the REST API which includes the GUID as well as the user's ID and encrypted password (both based on the webserver's CRM DB) in the URL. Not sure whether anything is possible by including the hashed password and am currently not doing anything with it. The REST API queries the DB using the GUID and webserver's user ID and returns the REST API's users ID and the webserver stores it in a session. The REST API receives the GUID and potentially the REST API's user ID and queries the DB to retrieve the account and potentially user before executing the route, and returns the response to the webserver which it returns it to the browser. The new approach might be something like the following: Before the webserver forwards any request to the REST API, it checks if a session is set, and if not performs a GET request to the REST API along with the GUID and if known user's credentials in the URL and receives a JWT which contains a payload including the account PK, and potentially the user PK, user's access level, etc. All future requests include this JWT in the header. The REST API no longer queries the DB to get the account ID and user authorized settings as it is provided in the JWT. A couple of questions: What should be done if a non-logged on user first accesses a public route, gets a JWT, and stores it in a session, but then later logs on and accesses a private route? The webserver thinks it has a valid JWT and will send it but the REST API will then decrypt it and find there is no user it. One option is for the webserver to use two sessions, but this sounds kludgy. Or maybe the REST API returns some header which instructs the webserver to re-authenticate, but not sure if even an option, and if so how to cleanly prevent some loop. Also, would it be necessary to issue a new JWT or can the payload in a JWT be changed? Is GET appropriate for requesting the JWT's or should I use some other method? Is it appropriate to include the user's access level in the JWT payload? Will one need to wait until the JWT has expired before their access level changes? Any ideas how to deal with using the user's password on the CRM to also authenticate on the REST API? The GUID is probably secret enough for the application and if an issue, can just use the GUID and username. Am I going down an reasonable path and anything else obvious I should be considering? Thanks!
  8. plogsID=plogsID will always be true. SELECT * FROM blogs WHERE blogsStatus=1 AND blogsID=blogsID You have several options. See https://www.php.net/manual/en/pdo.prepare.php, but to get you started... $stmt = $db->query('SELECT * FROM blogs WHERE blogsStatus=1 AND blogsID=:blogsID'); $stmt->execute(['blogsID' => $blogsID]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $stmt = $db->query('SELECT * FROM blogs WHERE blogsStatus=1 AND blogsID=?'); $stmt->execute([$blogsID]); $result = $stmt->fetch(PDO::FETCH_ASSOC); Or you can use use bindParam() or bindValue(), but don't worry about that for now.
  9. Thanks kicken, Will definitely give this a try. Looks like every iteration of the fast internal loop, everything on the future queue will be executed in its entirety and care must still be made not to put too much on it at once. Thanks again for pointing me in this direction. <?php final class StreamSelectLoop implements LoopInterface { public function __construct() { $this->futureTickQueue = new FutureTickQueue(); //... } public function run() { $this->running = true; while ($this->running) { $this->futureTickQueue->tick(); $this->timers->tick(); // Future-tick queue has pending callbacks ... if (!$this->running || !$this->futureTickQueue->isEmpty()) { $timeout = 0; // There is a pending timer, only block until it is due ... } elseif ($scheduledAt = $this->timers->getFirst()) { //... } //... } } //... } <?php final class FutureTickQueue { // ... /** * Flush the callback queue. */ public function tick() { // Only invoke as many callbacks as were on the queue when tick() was called. $count = $this->queue->count(); while ($count--) { \call_user_func( $this->queue->dequeue() ); } } }
  10. Sorry, questions. Inside what event loop? Some periodic timer I create using addPeriodicTimer(), and not the core reactphp loop, right? I don't believe I could (or should) create multiple loops, and I am also using the same loop for a socket connection. Your solution would potentially block for up to 1 second, true? Is that likely too long? Are there good rules of thumb for the loop period to maximum block time ratio? // setup $worker = new DoesWork(); // inside the event loop, if ($worker->hasWork()) { $worker->doWork(); } <?php $loop = \React\EventLoop\Factory::create(); $server = new \React\Socket\TcpServer("$ip:$port", $loop); $loop->addPeriodicTimer(5, function () { // 5 second loop }); $loop->addPeriodicTimer(1, function () { // 1 second loop }); $loop->run();
  11. Thanks requinix, Didn't know that about generators. They've made me rethink things a few times. Yes, I now know for sure that React is not truly asynchronous, and had to create the initial script I posted just to prove it to myself. The last script I showed was a totally different process which checks a queue, and my reactphp script would lpush() work into a queue, and thus not be blocked. Ah, I get your idea. Specify the amount of time to dedicate to it and do no more. Seems simpler that a queue.
  12. Not sure if a generator would help. It might save memory but didn't think they had anything to do with not blocking. Am I wrong? I am sure I can split up doWork's work, but how much is enough? Would be nice to not have to worry about it. Any thoughts on my new process or queue idea? For the queue, is it typical to also include a loop which checks it periodically checks it? Or maybe something really simple like the following? while(true) { if($workToBeDone = $redis->rpop('workToBeDone')) { // process the work } else { sleep(1); } }
  13. Occasionally bla::doWork() under test case 0 will send out a group of emails and and take a little time, and while ReactPHP provides async i/o, doWork() is blocking and prevents the other loops from triggering. I came up with two other possible solutions under my test case 1 and 2. As another solution, thinking of using a redis/etc queue to dump the content to in this script and then have another script continuously run and process the data when it becomes available. Any recommendations which of my three solutions are best, or whether I should do something all together different? Thanks <?php ini_set('display_errors', 1); require __DIR__.'/../../vendor/autoload.php'; class bla { private $loop, $time, $onesecondCounter=0, $fivesecondCounter=0, $processCounter=0; public function __construct($test) { $this->time = microtime(true); $this->loop = \React\EventLoop\Factory::create(); $phpScript = 'php -r "echo(\"start work\n\");sleep(2);echo(\"end work\n\");"'; $this->loop->addPeriodicTimer(5, function () use($test, $phpScript) { $this->fivesecondCounter++; $this->test('5 second loop', 5*$this->fivesecondCounter); switch($test) { case 0: $this->doWork(); break; case 1: $process = new \React\ChildProcess\Process($phpScript); $process->start($this->loop); $process->stdout->on('data', function ($chunk) { echo '$process->stdout->on: '.$chunk. PHP_EOL; }); $process->on('exit', function($exitCode, $termSignal) use ($process) { $this->processCounter++; echo 'Process exited with code ' . $exitCode .' and termSignal ' . $termSignal. PHP_EOL; $process->terminate(); }); break; case 2: echo 'start exec'.PHP_EOL; shell_exec("/usr/bin/nohup ".$phpScript." >/dev/null 2>&1 &"); echo 'end exec'.PHP_EOL; break; case 3: echo 'Or use another approach such as a redis queue?'.PHP_EOL; break; default: exit("invalid $test"); } }); $this->loop->addPeriodicTimer(1, function () { $this->onesecondCounter++; $this->test('1 second loop', $this->onesecondCounter); }); } public function run() { $this->loop->run(); } private function doWork() { $t=microtime(true); $c=0; for($i=0; $i<=10000000; $i++ ) { $c++; } printf('Just did %.2f seconds of work'.PHP_EOL, microtime(true)-$t); } private function test($msg, $c) { $time = microtime(true) - $this->time; printf('msg: %s mS: %.2f Error: %.2f onesecondCounter: %d, fivesecondCounter %d, processCounter %d'.PHP_EOL, $msg ,100*$time, $time - $c, $this->onesecondCounter, $this->fivesecondCounter, $this->processCounter); } } $bla = new bla($argv[1]??0); $bla->run();
  14. Is there a better way to have array_intersect_key() return only non-NULL values? Thanks function stripAssociatedArray(array $array, array $keys, bool $removeNull=false):array { $array = array_intersect_key($array, array_flip($keys)); return $removeNull?array_filter($array, fn($value) => !is_null($value)):$array; }
  15. Are you not getting the appropriate output? If so add some test to validate what you are seeing. $stmt = $ConnectingDB->query($sql); print_r($stmt->fetchAll()); Or are your links not working? Has to do with your JavaScript. <ol class="carousel-indicators"> <li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li> <li data-target="#carouselExampleIndicators" data-slide-to="1"></li> <li data-target="#carouselExampleIndicators" data-slide-to="2"></li> </ol>
  16. I would do it this way $countries = array_intersect_key($countries, ['US'=>null, 'CA'=>null]); unless I wanted to be super tricky in which I would do it this way $countries = array_intersect_key($countries, array_flip(['US', 'CA'])); gizmola's way is defiantly more tricky than mine.
  17. Thanks benanamen, While it is typically hardcoded , suppose so is a config.ini or config.json file, and as long as a single dedicated file, makes perfect sense to me. Ah, didn't think of dotenv, and think it could be a great approach as well.
  18. Every now and then, I experience some strange behavior and eventually trace it back to parse_ini_file's scanner_mode being applicable to some parameter but not another, and am looking for alternatives. Below are several of my thoughts. How do you store configuration settings? Do you use different approaches based on the application, and if so what criteria governs which you use? Keep with a ini file with parse_ini_file. Obviously, not working for me. An ini file but with a class dedicated to ensuring the data is appropriate. Seems like too much duplicating content which will result in errors. YAML. Don't think I want to. XML. Not as readable. In a database. Maybe, but might be harder to manage. Hardcode an array in PHP. Probably not. JSON. I like the idea, but feel comments are important in a config file, and am considering the following: Add extra valid JSON properties which contain comments. Don't like the idea. Use JavaScript's JSON.stringify. Too much mixing technologies. Add comments to the JSON and then strip them using a 3rd party parser such as https://json5.org/ or a little regex. My main issue is inability to auto-format, but this seems viable. Any other ideas? Thanks!
  19. Oops. Looked for a way to delete my 3v4l post or even ask someone to do so, but no luck. Wednesday 2020-09-02 01:02:03 to Friday 2020-09-04 04:05:06 is the closest to the gap.
  20. Try these dates Fill Wednesday 1978-09-27 12:34:56 -> Tuesday 2020-09-08 12:34:56 Gap Wednesday 2020-09-09 01:02:03 -> Friday 2020-09-11 04:05:06 New Wednesday 1978-10-04 01:02:03 -> Friday 1978-10-06 04:05:06 When I view your link, I see one output, but with mine, I see dozens. Your output: My Output
  21. Hi requnix, Yes, saw it. I like yours more than my original version as it is more concise, but I think it doesn't select the right dates if the fill range is earlier than the gap range. Any slicker ways to do it, or just put some if fill > gap, do what you show, else do something similar as necessary? By the way, how do you set https://3v4l.org/0RJfq up so it does not try every version of PHP? Of topic, but your solution provides different results if given DateTimeImmutable instead of DateTime. If this wasn't just a quick example, would you typically write script which would work for both. Thanks
  22. Oops! Looks like I failed to say that new fill must also match the gap's duration. Sorry bout that. What had been originally messing me up were these two lines (but my original posted showed the DateInterval as being commented out as it didn't work). I create a DateInterval for my desired duration, determine either the appropriate modified fill start or end date, and then get the other associated modified fill date by applying the gap DateInterval, and it mostly works but not always. $fillStartModified = $fillEndModified->sub($gapStart->diff($gapEnd)); $fillEndModified = $fillStartModified->add($gapStart->diff($gapEnd)); Which is basically what you and kicken indicated on an earlier post. DateIntervals do not represent an absolute duration but are based on what they are applied to. Makes me more appreciate all PHP's date functions are doing. I think my original solution is good enough, but just for the heck of it have provided another narrative. Given gap: The gap starts on a Thursday at 00:15:00 and ends on a Sunday at 13:00:00, and there are 31 days, 12 hours, and 45 minutes between the two dates. gapStart 2019-05-23 00:15:00 Thu gapEnd 2019-06-23 13:00:00 Sun Given constraint: The modified fill start and end must fall withing the original fill start and end time, and should be selected so it is as close to the gap as possible. fillStart 2019-07-23 00:15:00 Tue fillEnd 2019-09-23 13:00:00 Mon Results: The modified fill also starts on a Thursday at 00:15:00 and ends on a Sunday at 13:00:00, and there are also 31 days, 12 hours, and 45 minutes between the two dates. It also falls within the given fill start and end time, and is positioned toward the start of this time span in order to be as close to the gap as possible. fillStartModified 2019-07-25 00:15:00 Thu fillEndModified 2019-08-25 13:00:00 Sun
  23. You mean you can't read my mind to gain an understanding of how I define those words? Sorry requinix, my bad. Typically with PHP, we consider time the combination of the day/month/year plus the seconds past midnight, but for this application I am defining "WeekDayTime" as the day of the week (D) plus the seconds past midnight. For instance, Examples #1 to #4 are not the same WeekDayTime as Thu 2019-01-10 13:34:25 because the day of the week is different, #8 is not the same because the seconds past midnight are different, but #5 to #7 are the same because they both have the same day of the week and seconds past midnight. I am also defining "gap" as two DateTimes where the later is greater than the first, and "fill" as two other DateTimes again where the later is greater than the first. Applying this definition of WeekDayTime, fill, and gap, I am trying to determine the start and end DateTimes (not WeekDayTimes) which: fall within two other (fill) DateTimes. have the same number of seconds between them as two other (gap) DateTimes. have the same WeekDayTime as two other (gap) DateTimes. have the least number of seconds between the start time and the gap's start time (or the end time and the gap's end time which is the same) Sorry, still used "gap" and "fill", but hope it makes more sense now. Target Thu 2019-01-10 13:34:25 Example #1: Fri 2019-01-11 13:34:25 (P1D) Example #2: Wed 2019-01-16 13:34:25 (P6D) Example #3: Sun 2019-02-10 13:34:25 (P1M) Example #4: Fri 2020-01-10 13:34:25 (P1Y) Example #5: Thu 2019-01-17 13:34:25 (P7D) Example #6: Thu 2019-01-24 13:34:25 (P14D) Example #7: Thu 2019-05-02 13:34:25 (P112D) Example #8: Thu 2019-05-02 14:34:25 (P112DT1H) $dtTarget = new \DateTimeImmutable('@'.(time()-rand(600*24*60*60, 700*24*60*60))); echo(sprintf("Target %s\n\n", $dtTarget->format('D Y-m-d H:i:s'))); foreach(['P1D', 'P6D', 'P1M', 'P1Y', 'P7D', 'P14D', 'P112D', 'P112DT1H'] as $index=>$interval) { echo(sprintf("Example #%d: %s (%s)\n", $index+1, $dtTarget->add(new \DateInterval($interval))->format('D Y-m-d H:i:s'), $interval)); }
  24. Say I have a fill from January 1st to January 31st and from April 1st to April 30th. Don't have a calendar in front of me to confirm the week days, but if I had a gap from February 10th to the 12th, the closest would be latest time in January which ends on the same time/week as the February 12th gap end, and if I had a gap from March 20th to 22nd, the closest would be the earliest time in April which starts on the same time/week as the February 10th gap start.
  25. My birthday and anniversary? Wait, there's more dates. Been wanting a larger family and all my recent children's birthday?
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.