Example #1
0
 /**
  * Test method for testing the delete old database entries function
  *
  * @covers \Liebig\Cron\Cron::setDeleteDatabaseEntriesAfter
  * @covers \Liebig\Cron\Cron::run
  */
 public function testDeleteOldDatabaseEntries()
 {
     $manager1 = new \Liebig\Cron\Models\Manager();
     $date1 = new \DateTime();
     date_sub($date1, date_interval_create_from_date_string('240 hours'));
     $manager1->rundate = $date1;
     $manager1->runtime = 0.01;
     $this->assertNotNull($manager1->save());
     $newError1 = new \Liebig\Cron\Models\Job();
     $newError1->name = "test1";
     $newError1->return = "test1 fails";
     $newError1->runtime = 0.001;
     $newError1->cron_manager_id = $manager1->id;
     $this->assertNotNull($newError1->save());
     $newError2 = new \Liebig\Cron\Models\Job();
     $newError2->name = "test2";
     $newError2->return = "test2 fails";
     $newError2->runtime = 0.002;
     $newError2->cron_manager_id = $manager1->id;
     $this->assertNotNull($newError2->save());
     $manager2 = new \Liebig\Cron\Models\Manager();
     $date2 = new \DateTime();
     date_sub($date2, date_interval_create_from_date_string('240 hours'));
     $manager2->rundate = $date2;
     $manager2->runtime = 0.02;
     $this->assertNotNull($manager2->save());
     $newError3 = new \Liebig\Cron\Models\Job();
     $newError3->name = "test3";
     $newError3->return = "tes31 fails";
     $newError3->runtime = 0.003;
     $newError3->cron_manager_id = $manager2->id;
     $this->assertNotNull($newError3->save());
     $manager3 = new \Liebig\Cron\Models\Manager();
     $date3 = new \DateTime();
     date_sub($date3, date_interval_create_from_date_string('10 hours'));
     $manager3->rundate = $date3;
     $manager3->runtime = 0.03;
     $this->assertNotNull($manager3->save());
     $newError4 = new \Liebig\Cron\Models\Job();
     $newError4->name = "test4";
     $newError4->return = "test4 fails";
     $newError4->runtime = 0.004;
     $newError4->cron_manager_id = $manager3->id;
     $this->assertNotNull($newError4->save());
     $newError5 = new \Liebig\Cron\Models\Job();
     $newError5->name = "test5";
     $newError5->return = "test5 fails";
     $newError5->runtime = 0.005;
     $newError5->cron_manager_id = $manager3->id;
     $this->assertNotNull($newError5->save());
     $this->assertEquals(3, \Liebig\Cron\Models\Manager::count());
     $this->assertEquals(5, \Liebig\Cron\Models\Job::count());
     Cron::setDeleteDatabaseEntriesAfter(240);
     Cron::run();
     $this->assertEquals(2, \Liebig\Cron\Models\Manager::count());
     $this->assertEquals(2, \Liebig\Cron\Models\Job::count());
     Cron::setDeleteDatabaseEntriesAfter(0);
     $manager4 = new \Liebig\Cron\Models\Manager();
     $date4 = new \DateTime();
     date_sub($date4, date_interval_create_from_date_string('2400 hours'));
     $manager4->rundate = $date4;
     $manager4->runtime = 0.04;
     $this->assertNotNull($manager4->save());
     $newError6 = new \Liebig\Cron\Models\Job();
     $newError6->name = "test6";
     $newError6->return = "test6 fails";
     $newError6->runtime = 0.006;
     $newError6->cron_manager_id = $manager4->id;
     $this->assertNotNull($newError6->save());
     $newError7 = new \Liebig\Cron\Models\Job();
     $newError7->name = "test7";
     $newError7->return = "test7 fails";
     $newError7->runtime = 0.007;
     $newError7->cron_manager_id = $manager3->id;
     $this->assertNotNull($newError7->save());
     Cron::run();
     $this->assertEquals(4, \Liebig\Cron\Models\Manager::count());
     $this->assertEquals(4, \Liebig\Cron\Models\Job::count());
 }
Example #2
0
 /**
  * Run the cron jobs
  * This method checks and runs all the defined cron jobs at the right time
  * This method (route) should be called automatically by a server or service
  * 
  * @static
  * @param bool $checkRundateOnce optional When we check if a cronjob is due do we take into account the time when the run function was called ($checkRundateOnce = true) or do we take into account the time when each individual cronjob is executed ($checkRundateOnce = false) - default value is true
  * @return array Return an array with the rundate, runtime, errors and a result cron job array (with name, function return value, runtime in seconds)
  */
 public static function run($checkRundateOnce = true)
 {
     // If a new lock file is created, $overlappingLockFile will be equals the file path
     $overlappingLockFile = "";
     try {
         // Get the rundate
         $runDate = new \DateTime();
         // Fire event before the Cron run will be executed
         \Event::fire('cron.beforeRun', array($runDate->getTimestamp()));
         // Check if prevent job overlapping is enabled and create lock file if true
         $preventOverlapping = self::getConfig('preventOverlapping', false);
         if (is_bool($preventOverlapping) && $preventOverlapping) {
             $storagePath = "";
             // Fallback function for Laravel3 with helper function path('storage')
             if (function_exists('storage_path')) {
                 $storagePath = storage_path();
             } else {
                 if (function_exists('path')) {
                     $storagePath = path('storage');
                 }
             }
             if (!empty($storagePath)) {
                 $lockFile = $storagePath . DIRECTORY_SEPARATOR . 'cron.lock';
                 if (file_exists($lockFile)) {
                     self::log('warning', 'Lock file found - Cron is still running and prevent job overlapping is enabled - second Cron run will be terminated.');
                     if (self::isDatabaseLogging()) {
                         // Create a new cronmanager database object with runtime -1
                         $cronmanager = new \Liebig\Cron\Models\Manager();
                         $cronmanager->rundate = $runDate;
                         $cronmanager->runtime = -1;
                         $cronmanager->save();
                     }
                     // Fire the Cron locked event
                     \Event::fire('cron.locked', array('lockfile' => $lockFile));
                     // Fire the after run event, because we are done here
                     \Event::fire('cron.afterRun', array('rundate' => $runDate->getTimestamp(), 'inTime' => -1, 'runtime' => -1, 'errors' => 0, 'crons' => array(), 'lastRun' => array()));
                     return array('rundate' => $runDate->getTimestamp(), 'inTime' => -1, 'runtime' => -1, 'errors' => 0, 'crons' => array(), 'lastRun' => array());
                 } else {
                     // Create lock file
                     touch($lockFile);
                     if (!file_exists($lockFile)) {
                         self::log('error', 'Could not create Cron lock file at ' . $lockFile . '.');
                     } else {
                         // Lockfile created successfully
                         // $overlappingLockFile is used to delete the lock file after Cron run
                         $overlappingLockFile = $lockFile;
                     }
                 }
             } else {
                 self::log('error', 'Could not get the path to the Laravel storage directory.');
             }
         }
         // Get the run interval from Laravel config
         $runInterval = self::getRunInterval();
         // Getting last run time only if database logging is enabled
         if (self::isDatabaseLogging()) {
             // Get the time (in seconds) between this and the last run and save this to $timeBetween
             $lastManager = \Liebig\Cron\Models\Manager::orderBy('rundate', 'DESC')->take(1)->get();
             if (!empty($lastManager[0])) {
                 $lastRun = new \DateTime($lastManager[0]->rundate);
                 $timeBetween = $runDate->getTimestamp() - $lastRun->getTimestamp();
             } else {
                 // No previous cron job runs were found
                 $timeBetween = -1;
             }
         } else {
             // If database logging is disabled
             // Cannot check if the cron run is in time
             $inTime = -1;
         }
         // Initialize the job and job error array and start the runtime calculation
         $allJobs = array();
         $errorJobs = array();
         $beforeAll = microtime(true);
         // Should we check if the cron expression is due once at method call
         if ($checkRundateOnce) {
             $checkTime = $runDate;
         } else {
             // or do we compare it to 'now'
             $checkTime = 'now';
         }
         // For all defined cron jobs run this
         foreach (self::$cronJobs as $job) {
             // If the job is enabled and if the time for this job has come
             if ($job['enabled'] && $job['expression']->isDue($checkTime)) {
                 // Get the start time of the job runtime
                 $beforeOne = microtime(true);
                 // Run the function and save the return to $return - all the magic goes here
                 try {
                     $return = $job['function']();
                 } catch (\Exception $e) {
                     // If an uncaught exception occurs
                     $return = get_class($e) . ' in job ' . $job['name'] . ': ' . $e->getMessage();
                     self::log('error', get_class($e) . ' in job ' . $job['name'] . ': ' . $e->getMessage() . "\r\n" . $e->getTraceAsString());
                 }
                 // Get the end time of the job runtime
                 $afterOne = microtime(true);
                 // If the function returned not null then we assume that there was an error
                 if (!is_null($return)) {
                     // Add to error array
                     array_push($errorJobs, array('name' => $job['name'], 'return' => $return, 'runtime' => $afterOne - $beforeOne));
                     // Log error job
                     self::log('error', 'Job with the name ' . $job['name'] . ' was run with errors.');
                     // Fire event after executing a job with erros
                     \Event::fire('cron.jobError', array('name' => $job['name'], 'return' => $return, 'runtime' => $afterOne - $beforeOne, 'rundate' => $runDate->getTimestamp()));
                 } else {
                     // Fire event after executing a job successfully
                     \Event::fire('cron.jobSuccess', array('name' => $job['name'], 'runtime' => $afterOne - $beforeOne, 'rundate' => $runDate->getTimestamp()));
                 }
                 // Push the information of the ran cron job to the allJobs array (including name, return value, runtime)
                 array_push($allJobs, array('name' => $job['name'], 'return' => $return, 'runtime' => $afterOne - $beforeOne));
             }
         }
         // Get the end runtime after all cron job executions
         $afterAll = microtime(true);
         // If database logging is enabled, save manager und jobs to db
         if (self::isDatabaseLogging()) {
             // Create a new cronmanager database object for this run and save it
             $cronmanager = new \Liebig\Cron\Models\Manager();
             $cronmanager->rundate = $runDate;
             $cronmanager->runtime = $afterAll - $beforeAll;
             $cronmanager->save();
             // If the Cron run in time check is enabled, verify the time between the current and the last Cron run ($timeBetween) and compare it with the run interval
             if (self::isInTimeCheck()) {
                 $inTime = false;
                 // Check if the run between this and the last run is in time (30 seconds tolerance) and log this event
                 if ($timeBetween === -1) {
                     self::log('notice', 'Cron run with manager id ' . $cronmanager->id . ' has no previous managers.');
                     $inTime = -1;
                 } elseif ($runInterval * 60 - $timeBetween < -30) {
                     self::log('error', 'Cron run with manager id ' . $cronmanager->id . ' is with ' . $timeBetween . ' seconds between last run too late.');
                     $inTime = false;
                 } elseif ($runInterval * 60 - $timeBetween > 30) {
                     self::log('error', 'Cron run with manager id ' . $cronmanager->id . ' is with ' . $timeBetween . ' seconds between last run too fast.');
                     $inTime = false;
                 } else {
                     self::log('info', 'Cron run with manager id ' . $cronmanager->id . ' is with ' . $timeBetween . ' seconds between last run in time.');
                     $inTime = true;
                 }
             } else {
                 $inTime = -1;
             }
             if (self::isLogOnlyErrorJobsToDatabase()) {
                 // Save error jobs only to database
                 self::saveJobsFromArrayToDatabase($errorJobs, $cronmanager->id);
             } else {
                 // Save all jobs to database
                 self::saveJobsFromArrayToDatabase($allJobs, $cronmanager->id);
             }
             // Log the result of the cron run
             if (empty($errorJobs)) {
                 self::log('info', 'The cron run with the manager id ' . $cronmanager->id . ' was finished without errors.');
             } else {
                 self::log('error', 'The cron run with the manager id ' . $cronmanager->id . ' was finished with ' . count($errorJobs) . ' errors.');
             }
             // Check for old database entires and delete them
             self::deleteOldDatabaseEntries();
         } else {
             // If database logging is disabled
             // Log the status of the cron job run without the cronmanager id
             if (empty($errorJobs)) {
                 self::log('info', 'Cron run was finished without errors.');
             } else {
                 self::log('error', 'Cron run was finished with ' . count($errorJobs) . ' errors.');
             }
         }
         // Removing overlapping lock file if lockfile was created
         if (!empty($overlappingLockFile)) {
             self::deleteLockFile($overlappingLockFile);
         }
         $returnArray = array('rundate' => $runDate->getTimestamp(), 'inTime' => $inTime, 'runtime' => $afterAll - $beforeAll, 'errors' => count($errorJobs), 'crons' => $allJobs);
         // If Cron was called before, add the latest call to the $returnArray
         if (isset($lastManager[0]) && !empty($lastManager[0])) {
             $returnArray['lastRun'] = array('rundate' => $lastManager[0]->rundate, 'runtime' => $lastManager[0]->runtime);
         } else {
             $returnArray['lastRun'] = array();
         }
         // Fire event after the Cron run was executed
         \Event::fire('cron.afterRun', $returnArray);
         // Return the cron jobs array (including rundate, in-time boolean, runtime in seconds, number of errors and an array with the cron jobs reports)
         return $returnArray;
     } catch (\Exception $ex) {
         // Removing overlapping lock file if lockfile was created
         if (!empty($overlappingLockFile)) {
             self::deleteLockFile($overlappingLockFile);
         }
         throw $ex;
     }
 }