jayeshmepani/swiss-ephemeris-ffi
PHP 8.3+ FFI wrapper for the Swiss Ephemeris C library. Exposes all 106 public API functions with 1:1 constant/signature parity and zero abstraction. No swetest shelling; outputs verified for parity via PHPUnit against swetest.
Installation
composer require jayeshmepani/swiss-ephemeris-ffi
libswisseph-dev) is required unless you need custom builds.Verify FFI Compatibility
Ensure PHP 8.3+ with FFI extension enabled (php -m | grep ffi). No additional composer.json config is needed for bundled libraries.
First Use Case: Planetary Positions
use SwissEphemeris\SwissEphemeris;
$ephemeris = new SwissEphemeris();
$julianDate = 2459288.5; // Example: 2021-03-01 00:00:00 UTC
$planet = SwissEphemeris::PLUTO;
$result = $ephemeris->swe_calc_ut($planet, $julianDate, 0);
echo "Pluto's position: " . implode(', ', $result);
Facade Usage (v1.1.1 Fix)
use SwissEphemeris\Facades\SwissEphemeris as Ephemeris;
$result = Ephemeris::swe_calc_ut(SwissEphemeris::PLUTO, $julianDate, 0);
// Note: Facade returns raw FFI buffers (no Laravel reshaping).
Direct C Function Calls (Unchanged)
$ephemeris->setPath('/custom/path'); // Singleton behavior: path must be set before first use.
$position = $ephemeris->swe_calc_ut(SwissEphemeris::MARS, $julianDate, 0);
Bundled Libraries (New)
vendor/jayeshmepani/swiss-ephemeris-ffi/resources/.php artisan vendor:publish --provider="Jayeshmepani\SwissEphemeris\SwissEphemerisServiceProvider" --tag="swisseph-libs"
Files are published to storage/app/swisseph/ (not public/).House Systems (Unchanged)
$houses = $ephemeris->swe_houses(
SwissEphemeris::HOUSE_WHITESIDE,
$julianDate,
52.5200, // latitude
13.4050, // longitude
0
);
Laravel Service Provider (Updated) Bind the singleton instance with path awareness:
public function register()
{
$this->app->singleton(SwissEphemeris::class, function ($app) {
$ephemeris = new SwissEphemeris();
$ephemeris->setPath(storage_path('app/swisseph')); // Optional: Use published libs
return $ephemeris;
});
}
Caching (Unchanged)
Cache::remember("astro_{$julianDate}_{$planet}", now()->addHours(1), function () use ($ephemeris, $planet, $julianDate) {
return $ephemeris->swe_calc_ut($planet, $julianDate, 0);
});
Error Handling (Enhanced)
try {
$result = $ephemeris->swe_calc_ut($planet, $julianDate, 0);
} catch (FFI\Exception\RuntimeException $e) {
Log::error("Swiss Ephemeris FFI error: " . $e->getMessage());
// Check if library path is misconfigured.
}
Singleton Path Limitation (New)
setPath() method must be called before any FFI operation. Subsequent calls to setPath() after initialization are ignored.Facade Clarification (Fixed in v1.1.1)
array{float, float}) are returned directly. Example:
$raw = Ephemeris::swe_calc_ut(SwissEphemeris::SUN, $julianDate, 0);
// $raw[0] = longitude (radians), $raw[1] = latitude (radians).
Library Publishing (New)
storage/app/swisseph/. Ensure the directory is writable:
mkdir -p storage/app/swisseph
chmod -R 755 storage/app/swisseph
Thread Safety (Unchanged)
Library Path Validation
if (!$ephemeris->swe_get_ephe_path('plu03.trop')) {
throw new \RuntimeException("Ephemeris file not found. Check path: " . $ephemeris->getPath());
}
FFI Buffer Inspection
Use var_dump() to inspect raw FFI outputs:
var_dump($ephemeris->swe_calc_ut(SwissEphemeris::MERCURY, $julianDate, 0));
// Expected: array{float, float} (longitude, latitude in radians).
Custom Library Paths Extend the service provider to support environment-based paths:
$this->app->singleton(SwissEphemeris::class, function () {
$ephemeris = new SwissEphemeris();
$ephemeris->setPath(config('astro.ephemeris_path', storage_path('app/swisseph')));
return $ephemeris;
});
Event-Driven Calculations Dispatch events for house/planet calculations:
event(new AstroEvent(
$ephemeris->swe_houses(SwissEphemeris::HOUSE_PLACIDUS, $julianDate, $lat, $lon, 0),
$julianDate
));
Artisan Commands (Unchanged)
php artisan astro:calculate --planet=venus --date="2023-10-15"
Use the published libraries by default:
$ephemeris->setPath(storage_path('app/swisseph'));
How can I help you explore Laravel packages today?