cahuk/server-timezone

Compatible Clock that gets server time from IP-based geolocation

v1.0.0 2025-05-29 13:50 UTC

This package is auto-updated.

Last update: 2025-06-29 14:35:54 UTC


README

A PHP package to resolve the current time based on an IP address using various external GeoIP and time APIs.

Installation

composer require cahuk/server-timezone

Running Unit Tests

Before running tests, clone the repository and install dependencies:

composer install
composer test

Usage Example

Below is a single comprehensive example demonstrating different approaches to resolve time based on IP:

<?php

require_once __DIR__ . '/../vendor/autoload.php';

use Psr\Http\Message\ResponseInterface;
use Cahuk\ServerTimezone\Core\Services\TimeResolverService;
use Cahuk\ServerTimezone\Infrastructure\TimeProvider\TimeResolverProvider;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\GeoIpTimeApiResolver;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\HttpClient\HttpConfig;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\IpFetcher\Api\IpApiFetcher;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\Services\TimeApiFetcherService;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\TimeFetcher\Api\HttpTimeFetcher;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\IpFetcher\Api\HttpIpFetcherResponse;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\IpFetcher\Api\Mappers\IpifyOrgIpMapper;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\TimeFetcher\Api\Mappers\TimeapiIoTimeMapper;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\IpFetcher\Api\HttpIpFetcherResponseInterface;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\IpFetcher\Api\Client\GuzzleHttpIpFetcherClient;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\TimeFetcher\Api\Mappers\IpgeolocationIoTimeMapper;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\TimeFetcher\Api\Client\GuzzleHttpTimeFetcherClient;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\IpFetcher\Api\HttpIpFetcherResponseMapperInterface;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\Services\TimeParametersMappers\DefaultMappingFields;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\Services\TimeParametersMappers\DefaultTimeParametersMappers;
use Cahuk\ServerTimezone\Infrastructure\TimeResolver\GeoIpTimeResolver\Services\TimeParametersMappers\DefaultMappingTimeParameters;

// ------------------------------------------------------------
// Example 1: Fetch current time for a known external IP address

$timeServerHost1    = 'https://timeapi.io';
$timeServerUrl1     = '/api/time/current/ip';
$timeServerHeaders1 = ['Accept' => 'application/json'];
$timeServerParams1  = ['ipAddress' => '89.151.31.13'];

$apiTimeProvider1 = new TimeResolverProvider(
    new GeoIpTimeApiResolver(
        new HttpTimeFetcher(
            new GuzzleHttpTimeFetcherClient(
                new HttpConfig(
                    $timeServerHost1,
                    $timeServerUrl1,
                    headers: $timeServerHeaders1,
                    parameters: $timeServerParams1,
                ),
                new IpgeolocationIoTimeMapper()
            )
        )
    )
);

var_dump((new TimeResolverService($apiTimeProvider1))->resolveTime()->now());

// ------------------------------------------------------------
// Example 2: Use separate services to fetch IP and then time by IP

$ipServerHost2 = 'https://api.ipify.org';
$ipServerUrl2  = '';

$timeServerHost2    = 'https://timeapi.io';
$timeServerUrl2     = '/api/time/current/ip';
$timeServerHeaders2 = ['Accept' => 'application/json'];

$apiTimeProvider2 = new TimeResolverProvider(
    new GeoIpTimeApiResolver(
        new TimeApiFetcherService(
            new IpApiFetcher(
                new GuzzleHttpIpFetcherClient(
                    new HttpConfig($ipServerHost2, $ipServerUrl2),
                    new IpifyOrgIpMapper()
                )
            ),
            new GuzzleHttpTimeFetcherClient(
                new HttpConfig(
                    $timeServerHost2,
                    $timeServerUrl2,
                    headers: $timeServerHeaders2
                ),
                new TimeapiIoTimeMapper()
            ),
            new DefaultTimeParametersMappers([
                DefaultMappingFields::IPV4->value => 'ipAddress'
            ]),
            new DefaultMappingTimeParameters()
        )
    )
);

var_dump((new TimeResolverService($apiTimeProvider2))->resolveTime()->now());

// ------------------------------------------------------------
// Example 3: Inline anonymous mapper for IP fetch response

$ipServerHost3    = 'https://ifconfig.me';
$ipServerHeaders3 = [
    'User-Agent' => 'curl/7.64.1',
    'Accept'     => 'text/plain',
];

$timeServerHost3    = 'https://timeapi.io';
$timeServerUrl3     = '/api/time/current/ip';
$timeServerHeaders3 = ['Accept' => 'application/json'];

$apiTimeProvider3 = new TimeResolverProvider(
    new GeoIpTimeApiResolver(
        new TimeApiFetcherService(
            new IpApiFetcher(
                new GuzzleHttpIpFetcherClient(
                    new HttpConfig($ipServerHost3, headers: $ipServerHeaders3),
                    new class implements HttpIpFetcherResponseMapperInterface {
                        #[\Override]
                        public function map(ResponseInterface $response): HttpIpFetcherResponseInterface
                        {
                            $body = $response->getBody();
                            $responseData = (string)$body;
                            return new HttpIpFetcherResponse($responseData);
                        }
                    }
                )
            ),
            new GuzzleHttpTimeFetcherClient(
                new HttpConfig(
                    $timeServerHost3,
                    $timeServerUrl3,
                    headers: $timeServerHeaders3
                ),
                new TimeapiIoTimeMapper()
            ),
            new DefaultTimeParametersMappers([
                DefaultMappingFields::IPV4->value => 'ipAddress'
            ]),
            new DefaultMappingTimeParameters()
        )
    )
);

var_dump((new TimeResolverService($apiTimeProvider3))->resolveTime()->now());

// ------------------------------------------------------------
// Example 4: Direct time fetch from IP-based service (premium key required)

$timeServerHost4    = 'https://api.ipgeolocation.io';
$timeServerUrl4     = '/v2/ipgeo';
$timeServerParams4  = ['apiKey' => 'YOUR_API_KEY_HERE']; // Replace with your premium API key

$apiTimeProvider4 = new TimeResolverProvider(
    new GeoIpTimeApiResolver(
        new HttpTimeFetcher(
            new GuzzleHttpTimeFetcherClient(
                new HttpConfig(
                    $timeServerHost4,
                    $timeServerUrl4,
                    parameters: $timeServerParams4
                ),
                new IpgeolocationIoTimeMapper()
            )
        )
    )
);

// Uncomment to test with valid API key:
// var_dump((new TimeResolverService($apiTimeProvider4))->resolveTime()->now());

Notes

  • Replace YOUR_API_KEY_HERE in Example 4 with your valid premium API key.
  • The examples demonstrate flexibility in using different IP and time services.
  • You can extend or customize mappers to fit different API response formats.

Running the example script

php ./examples/example.php