PHP Integration

Complete guide to integrating CronMonitor with your PHP applications.

Basic Integration #

Using file_get_contents() #

Simplest method for basic use cases:

<?php
// Your cron job logic
performBackup();

// Ping CronMonitor
$pingUrl = 'https://cronmonitor.io/ping/your-monitor-id';
file_get_contents($pingUrl);

More reliable with timeout and error handling:

<?php
function pingCronMonitor($monitorId) {
    $url = "https://cronmonitor.io/ping/{$monitorId}";

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    curl_close($ch);

    return $httpCode === 200;
}

// Your cron job
try {
    performBackup();
    pingCronMonitor('your-monitor-id');
} catch (Exception $e) {
    // Don't ping on failure
    error_log('Backup failed: ' . $e->getMessage());
}

With Guzzle HTTP Client #

If you're using Guzzle in your project:

<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

function pingCronMonitor(string $monitorId): bool
{
    $client = new Client([
        'timeout' => 10,
        'verify' => true, // SSL verification
    ]);

    try {
        $response = $client->get("https://cronmonitor.io/ping/{$monitorId}");
        return $response->getStatusCode() === 200;
    } catch (GuzzleException $e) {
        error_log('CronMonitor ping failed: ' . $e->getMessage());
        return false;
    }
}

// Usage
performBackup();
pingCronMonitor('your-monitor-id');

Symfony Integration #

Command with CronMonitor #

<?php
// src/Command/BackupDatabaseCommand.php

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

#[AsCommand(
    name: 'app:backup-database',
    description: 'Backup database with CronMonitor integration'
)]
class BackupDatabaseCommand extends Command
{
    public function __construct(
        private HttpClientInterface $httpClient,
        private string $cronMonitorId
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        try {
            // Your backup logic
            $this->performBackup();

            $output->writeln('Backup completed successfully');

            // Ping CronMonitor
            $this->pingCronMonitor();

            return Command::SUCCESS;
        } catch (\Exception $e) {
            $output->writeln('Backup failed: ' . $e->getMessage());
            return Command::FAILURE;
        }
    }

    private function performBackup(): void
    {
        // Your backup implementation
    }

    private function pingCronMonitor(): void
    {
        try {
            $this->httpClient->request('GET', 
                "https://cronmonitor.io/ping/{$this->cronMonitorId}",
                ['timeout' => 10]
            );
        } catch (\Exception $e) {
            // Log but don't fail the command
            error_log('CronMonitor ping failed: ' . $e->getMessage());
        }
    }
}

Configuration (services.yaml) #

# config/services.yaml
services:
    App\Command\BackupDatabaseCommand:
        arguments:
            $cronMonitorId: '%env(CRONMONITOR_BACKUP_ID)%'

Environment Variables #

# .env
CRONMONITOR_BACKUP_ID=abc123def456

Crontab Entry #

# Crontab
0 2 * * * cd /var/www/app && php bin/console app:backup-database >> /var/log/backup.log 2>&1

Laravel Integration #

Artisan Command #

<?php
// app/Console/Commands/BackupDatabase.php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;

class BackupDatabase extends Command
{
    protected $signature = 'backup:database';
    protected $description = 'Backup database with CronMonitor';

    public function handle()
    {
        try {
            // Your backup logic
            $this->performBackup();

            $this->info('Backup completed successfully');

            // Ping CronMonitor
            $this->pingCronMonitor();

            return 0;
        } catch (\Exception $e) {
            $this->error('Backup failed: ' . $e->getMessage());
            return 1;
        }
    }

    private function performBackup()
    {
        // Your backup implementation
    }

    private function pingCronMonitor()
    {
        $monitorId = config('services.cronmonitor.backup_id');

        try {
            Http::timeout(10)
                ->get("https://cronmonitor.io/ping/{$monitorId}");
        } catch (\Exception $e) {
            \Log::warning('CronMonitor ping failed', [
                'error' => $e->getMessage()
            ]);
        }
    }
}

Laravel Scheduler #

<?php
// app/Console/Kernel.php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('backup:database')
            ->daily()
            ->at('02:00')
            ->timezone('Europe/Warsaw');
    }
}

Configuration #

<?php
// config/services.php

return [
    'cronmonitor' => [
        'backup_id' => env('CRONMONITOR_BACKUP_ID'),
        'cleanup_id' => env('CRONMONITOR_CLEANUP_ID'),
    ],
];

Reusable Service #

<?php
// app/Services/CronMonitorService.php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class CronMonitorService
{
    public function ping(string $monitorKey): bool
    {
        $monitorId = config("services.cronmonitor.{$monitorKey}");

        if (empty($monitorId)) {
            Log::warning("CronMonitor ID not configured for: {$monitorKey}");
            return false;
        }

        try {
            $response = Http::timeout(10)
                ->retry(3, 100)
                ->get("https://cronmonitor.io/ping/{$monitorId}");

            return $response->successful();
        } catch (\Exception $e) {
            Log::error('CronMonitor ping failed', [
                'monitor' => $monitorKey,
                'error' => $e->getMessage()
            ]);

            return false;
        }
    }
}

// Usage in command:
public function handle(CronMonitorService $cronMonitor)
{
    $this->performBackup();
    $cronMonitor->ping('backup_id');
}

Advanced Patterns #

Ping with Execution Time #

Send execution duration as query parameter:

<?php
$startTime = microtime(true);

// Your job
performHeavyTask();

$executionTime = round(microtime(true) - $startTime, 2);

// Ping with execution time
$url = "https://cronmonitor.io/ping/{$monitorId}?duration={$executionTime}";
file_get_contents($url);

Ping with Status Message #

<?php
function pingWithStatus($monitorId, $status, $message = '') {
    $url = "https://cronmonitor.io/ping/{$monitorId}";
    $url .= "?status={$status}";

    if ($message) {
        $url .= "&message=" . urlencode($message);
    }

    file_get_contents($url);
}

// Usage
try {
    $result = performBackup();
    pingWithStatus('abc123', 'success', 'Backed up 1.2GB');
} catch (Exception $e) {
    pingWithStatus('abc123', 'failed', $e->getMessage());
}

Wrapper Function #

Create a reusable wrapper:

<?php
class CronMonitor
{
    private string $monitorId;
    private float $startTime;

    public function __construct(string $monitorId)
    {
        $this->monitorId = $monitorId;
        $this->startTime = microtime(true);
    }

    public function success(string $message = ''): void
    {
        $this->ping('success', $message);
    }

    public function failure(string $message = ''): void
    {
        $this->ping('failure', $message);
    }

    private function ping(string $status, string $message): void
    {
        $duration = round(microtime(true) - $this->startTime, 2);

        $url = "https://cronmonitor.io/ping/{$this->monitorId}";
        $url .= "?status={$status}&duration={$duration}";

        if ($message) {
            $url .= "&message=" . urlencode($message);
        }

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_exec($ch);
        curl_close($ch);
    }
}

// Usage
$monitor = new CronMonitor('abc123def456');

try {
    performBackup();
    $monitor->success('Backup completed: 1.2GB');
} catch (Exception $e) {
    $monitor->failure($e->getMessage());
    throw $e;
}

Error Handling Best Practices #

Don't Let Monitoring Fail Your Job #

<?php
// ❌ Bad: Monitoring failure breaks the job
performBackup();
file_get_contents($pingUrl); // Throws exception if network fails

// βœ… Good: Job completes even if monitoring fails
try {
    performBackup();
    file_get_contents($pingUrl);
} catch (Exception $e) {
    // Log but continue
    error_log('CronMonitor ping failed: ' . $e->getMessage());
}

Timeout Configuration #

<?php
// Always set timeouts
$context = stream_context_create([
    'http' => [
        'timeout' => 10, // 10 seconds max
        'ignore_errors' => true
    ]
]);

file_get_contents($pingUrl, false, $context);

Testing #

Unit Test Example (PHPUnit) #

<?php
use PHPUnit\Framework\TestCase;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

class CronMonitorTest extends TestCase
{
    public function testSuccessfulPing()
    {
        // Mock HTTP response
        $mock = new MockHandler([
            new Response(200, [], 'OK'),
        ]);

        $handlerStack = HandlerStack::create($mock);
        $client = new Client(['handler' => $handlerStack]);

        $response = $client->get('https://cronmonitor.io/ping/test123');

        $this->assertEquals(200, $response->getStatusCode());
    }
}

Video Tutorial #

πŸ“Ί Watch: "PHP Integration - Complete Guide" (8 minutes)

πŸŽ₯

PHP Integration

Video coming soon

Topics covered:

  • Basic integration methods (1:30)
  • Symfony integration (2:30)
  • Laravel integration (2:30)
  • Best practices (1:30)

Complete Example #

Here's a production-ready example:

<?php
/**
 * Production-ready backup script with CronMonitor
 */

class DatabaseBackup
{
    private string $monitorId;
    private float $startTime;

    public function __construct(string $monitorId)
    {
        $this->monitorId = $monitorId;
        $this->startTime = microtime(true);
    }

    public function run(): void
    {
        try {
            echo "Starting backup...\n";

            // Perform backup
            $this->createBackup();

            // Upload to S3
            $this->uploadToS3();

            // Cleanup old backups
            $this->cleanupOldBackups();

            echo "Backup completed successfully\n";

            // Notify CronMonitor of success
            $this->notifySuccess();

        } catch (\Exception $e) {
            echo "Backup failed: {$e->getMessage()}\n";

            // Notify CronMonitor of failure
            $this->notifyFailure($e->getMessage());

            throw $e;
        }
    }

    private function createBackup(): void
    {
        exec('mysqldump -u root database > backup.sql', $output, $returnCode);

        if ($returnCode !== 0) {
            throw new \Exception('mysqldump failed');
        }
    }

    private function uploadToS3(): void
    {
        // S3 upload logic
    }

    private function cleanupOldBackups(): void
    {
        // Cleanup logic
    }

    private function notifySuccess(): void
    {
        $duration = round(microtime(true) - $this->startTime, 2);
        $size = round(filesize('backup.sql') / 1024 / 1024, 2);

        $this->ping("success", "Backup completed: {$size}MB in {$duration}s");
    }

    private function notifyFailure(string $error): void
    {
        $duration = round(microtime(true) - $this->startTime, 2);
        $this->ping("failure", "Failed after {$duration}s: {$error}");
    }

    private function ping(string $status, string $message): void
    {
        $url = "https://cronmonitor.io/ping/{$this->monitorId}";
        $url .= "?status={$status}&message=" . urlencode($message);

        try {
            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 10,
                CURLOPT_FOLLOWLOCATION => true,
            ]);
            curl_exec($ch);
            curl_close($ch);
        } catch (\Exception $e) {
            error_log('CronMonitor ping failed: ' . $e->getMessage());
        }
    }
}

// Run backup
$backup = new DatabaseBackup(getenv('CRONMONITOR_BACKUP_ID'));
$backup->run();

Next Steps #