Swiss Ephemeris PHP FFI β 106 Functions Complete Mapping
Introduction
100% precise 1:1 FFI mapping of the Swiss Ephemeris C library for PHP.
Overview
My package provides a complete PHP FFI binding to the Swiss Ephemeris C library. I wrap all 106 functions from the official library, giving you direct access to professional-grade astronomical calculations. The binary library (libswe.so/swe.dll) is automatically downloaded on composer install.
106 Functions
Complete 1:1 mapping of all Swiss Ephemeris C library functions
Built-in Moshier
Works without ephemeris files using built-in Moshier algorithm (~1 arcsecond precision)
Milliarcsecond Precision
Optional ephemeris files for research-grade accuracy
Laravel Ready
Includes service provider and facade for seamless Laravel integration
Requirements
- PHP 8.3+ β Required for typed constants, readonly classes
- FFI Extension β Required for Foreign Function Interface
- Swiss Ephemeris Binary β Automatically downloaded via composer
Quick Start
composer require jayeshmepani/swiss-ephemeris-ffi
Enable FFI
Add to your php.ini:
extension=ffi
ffi.enable=1
<?php
use SwissEph\FFI\SwissEphFFI;
$sweph = new SwissEphFFI();
$jd = $sweph->swe_julday(2000, 1, 1, 12.0, SwissEphFFI::SE_GREG_CAL);
$xx = $sweph->getFFI()->new("double[6]");
$serr = $sweph->getFFI()->new("char[256]");
$result = $sweph->swe_calc_ut(
$jd,
SwissEphFFI::SE_SUN,
SwissEphFFI::SEFLG_SPEED,
$xx,
$serr
);
echo "Sun Longitude: " . $xx[0] . "Β°\n";
echo "Sun Speed: " . $xx[3] . "Β°/day\n";
Installation & Setup
1. Install Package
composer require jayeshmepani/swiss-ephemeris-ffi
2. Enable FFI Extension
The FFI extension requires explicit enabling in php.ini. This is a security measure since FFI can execute arbitrary native code.
extension=ffi
ffi.enable=1
3. Laravel Integration
'providers' => [
SwissEph\Service\SwissEphServiceProvider::class,
],
'aliases' => [
'SwissEph' => SwissEph\Service\SwissEphFacade::class,
]
$jd = SwissEph::swe_julday(2000, 1, 1, 12.0, SwissEph::SE_GREG_CAL);
Platform-Specific Setup
Linux (Ubuntu/Debian)
# Install PHP FFI
sudo apt install php8.3-ffi
# Or for PHP 8.4
sudo apt install php8.4-ffi
Linux (CentOS/RHEL/Fedora)
sudo dnf install php-ffi
# or
sudo yum install php-ffi
macOS
# PHP from Homebrew includes FFI by default
brew install php@8.3
Windows
FFI is included with PHP 8.3+ for Windows. Just enable in php.ini as shown above.
Verify Installation
php -r "echo extension_loaded('ffi') ? 'FFI loaded' : 'FFI not loaded';"
php -r "echo ini_get('ffi.enable') ? 'FFI enabled' : 'FFI not enabled';"
Ephemeris Files (Optional)
Understanding the Two Components
- Binary (libswe.so / swe.dll) β Provided automatically by my package on composer install. Contains the calculation engine.
- Data files (.se1) β OPTIONAL. The C library has built-in Moshier algorithm (~1 arcsecond precision) that works WITHOUT any external files. For higher precision (milliarcsecond), download the ephemeris files.
Download Options
Option A β From My Releases (Recommended)
Option B β From Official Swiss Ephemeris
git clone --depth 1 --filter=blob:none --sparse https://github.com/aloistr/swisseph
cd swisseph
git sparse-checkout set ephe
File Types
| File | Description | Size |
|---|---|---|
sepl*.se1 |
Planet ephemeris (Sun-Pluto) | ~27 MB |
semo*.se1 |
Moon ephemeris | ~70 MB |
seas*.se1 |
Asteroid ephemeris (700,000+) | ~100 MB |
sepm*.se1 |
Planetary moon files | Varies |
sefstars.txt |
Fixed star names | Small |
seorbel.txt |
Fictitious planet orbital elements | Small |
Usage
$sweph = new SwissEphFFI();
$sweph->swe_set_ephe_path(__DIR__ . '/ephe');
Precision: Moshier vs Swiss Ephemeris
Run php examples/precision_demo.php to see the actual difference on your system.
Example Output (May 15, 1990, 14:30 UT)
This is actual output from running php examples/precision_demo.php on this system.
Without Ephemeris Files (Moshier - built-in)
Moon: 298.45007158851 Sun: 54.496555505961 Mercury: 38.000338678331 Venus: 12.937142407004 Mars: 348.4112182787 Jupiter: 99.561406572401 Saturn: 295.24767492246 Uranus: 279.18135006806 Neptune: 284.35136993429 Pluto: 226.16455507419
With Swiss Ephemeris Files (.se1)
Moon: 298.45019395505 Sun: 54.496545122771 Mercury: 38.000337751223 Venus: 12.937139147687 Mars: 348.41121491329 Jupiter: 99.561460556072 Saturn: 295.24764096018 Uranus: 279.18131332432 Neptune: 284.35134659349 Pluto: 226.16451075759
Difference (Swiss Eph - Moshier) β ACTUAL VALUES
Moon: 0.00012236653799391Β° = 0.44051953677808 arcseconds
Sun: -1.0383189994911E-5Β° = -0.037379483981681 arcseconds
Mercury: -9.2710805432716E-7Β° = -0.0033375889955778 arcseconds
Venus: -3.2593169319028E-6Β° = -0.01173354095485 arcseconds
Mars: -3.3654127946647E-6Β° = -0.012115486060793 arcseconds
Jupiter: 5.398367167686E-5Β° = 0.1943412180367 arcseconds
Saturn: -3.3962287091072E-5Β° = -0.12226423352786 arcseconds
Uranus: -3.6743742214185E-5Β° = -0.13227747197107 arcseconds
Neptune: -2.3340794655269E-5Β° = -0.084026860758968 arcseconds
Pluto: -4.4316591925053E-5Β° = -0.15953973093019 arcseconds
Summary Table
| Planet | Difference (arcsec) | Visual Impact |
|---|---|---|
| Moon | 0.44" | Can change sign near boundaries |
| Jupiter | 0.19" | Minor |
| Pluto | 0.16" | Minor |
| Saturn | 0.12" | Minor |
| Uranus | 0.13" | Minor |
| Neptune | 0.08" | Minor |
| Mars | 0.01" | Negligible |
| Venus | 0.01" | Negligible |
| Sun | 0.04" | Negligible |
| Mercury | 0.003" | Negligible |
Key Insights
- Moon shows the largest difference (~0.44 arcseconds). This can affect zodiac sign boundaries, especially close to 0Β° Aries, Cancer, Libra, Capricorn.
- Planets show smaller differences (0.003-0.2 arcseconds) but matter for precise calculations.
- Moshier provides ~1 arcsecond precision (good enough for most purposes)
When Does It Matter?
| Use Case | Moshier Enough? | Why |
|---|---|---|
| General horoscopes | Yes | 1 arcsecond is invisible to naked eye |
| Moon sign calculations | Maybe | 0.5 arcsec can change sign near boundaries |
| Medical astrology | No | Requires highest precision |
| Research/dates | No | Historical accuracy needed |
| Solar Returns | Yes | Sun position clear enough |
Complete Examples
Birth Chart (Tropical)
<?php
use SwissEph\FFI\SwissEphFFI;
$sweph = new SwissEphFFI();
$jd = $sweph->swe_julday(1990, 5, 15, 14.5, SwissEphFFI::SE_GREG_CAL);
$xx = $sweph->getFFI()->new('double[6]');
$serr = $sweph->getFFI()->new('char[256]');
$signs = ['Ari', 'Tau', 'Gem', 'Can', 'Leo', 'Vir', 'Lib', 'Sco', 'Sag', 'Cap', 'Aqu', 'Pis'];
$planets = [
'Sun' => SwissEphFFI::SE_SUN,
'Moon' => SwissEphFFI::SE_MOON,
'Mercury' => SwissEphFFI::SE_MERCURY,
'Venus' => SwissEphFFI::SE_VENUS,
'Mars' => SwissEphFFI::SE_MARS,
'Jupiter' => SwissEphFFI::SE_JUPITER,
'Saturn' => SwissEphFFI::SE_SATURN,
'Uranus' => SwissEphFFI::SE_URANUS,
'Neptune' => SwissEphFFI::SE_NEPTUNE,
'Pluto' => SwissEphFFI::SE_PLUTO,
];
echo "Birth Chart - May 15, 1990, 14:30 UT\n";
echo str_repeat("=", 40) . "\n\n";
foreach ($planets as $name => $p) {
$result = $sweph->swe_calc_ut($jd, $p, SwissEphFFI::SEFLG_SPEED, $xx, $serr);
$lon = $xx[0];
$signIdx = floor($lon / 30);
$deg = $lon % 30;
echo sprintf("%-8s %7.2fΒ° %3s %2.2fΒ° (%s)\n", $name . ':', $lon, $signs[$signIdx], $deg, $xx[3] . "Β°/day");
}
Sidereal Lahiri Ayanamsa
<?php
use SwissEph\FFI\SwissEphFFI;
$sweph = new SwissEphFFI();
$jd = $sweph->swe_julday(2000, 1, 1, 12.0, SwissEphFFI::SE_GREG_CAL);
$xx = $sweph->getFFI()->new('double[6]');
$serr = $sweph->getFFI()->new('char[256]');
$sweph->swe_set_sid_mode(SwissEphFFI::SE_SIDM_LAHIRI, 0, 0);
$ayan = $sweph->swe_get_ayanamsa($jd);
echo "Ayanamsa (Lahiri): " . $ayan . "Β°\n";
$result = $sweph->swe_calc_ut($jd, SwissEphFFI::SE_SUN, 0, $xx, $serr);
$tropical = $xx[0];
$sidereal = $tropical - $ayan;
if ($sidereal < 0) $sidereal += 360;
echo "Tropical Sun: " . $tropical . "Β°\n";
echo "Sidereal Sun: " . $sidereal . "Β°\n";
House Systems
<?php
use SwissEph\FFI\SwissEphFFI;
$sweph = new SwissEphFFI();
$jd = $sweph->swe_julday(2000, 1, 1, 12.0, SwissEphFFI::SE_GREG_CAL);
$hsys = $sweph->getFFI()->new("char[1]");
$hsys[0] = ord('P');
$cusps = $sweph->getFFI()->new("double[13]");
$ascmc = $sweph->getFFI()->new("double[10]");
$serr = $sweph->getFFI()->new("char[256]");
$sweph->swe_houses($jd, SwissEphFFI::SE_GREG_CAL, 23.2, 72.8, ord('P'), $cusps, $ascmc, $serr);
echo "Placidus House Cusps:\n";
for ($i = 1; $i <= 12; $i++) {
echo "House $i: " . $cusps[$i] . "Β°\n";
}
echo "\nAscendant: " . $ascmc[0] . "Β°\n";
echo "MC: " . $ascmc[1] . "Β°\n";
| Code | System |
|---|---|
'P' |
Placidus |
'K' |
Koch |
'R' |
Regiomontanus |
'C' |
Campanus |
'E' |
Equal |
'W' |
Whole Sign |
Function Coverage
My package provides 106 functions, which is 100% coverage of the official Swiss Ephemeris C library. This is more than competitors:
| Package | Functions | Coverage |
|---|---|---|
| swiss-ephemeris-ffi (mine) | 106 | 100% |
| php-sweph (cyjoelchen) | 100 | 94% |
| swephe-php (nastal) | 2 | 2% |
Function Categories
- Calendar & Time: swe_julday, swe_date_conv, etc. (4 functions)
- Planetary Calculations: swe_calc, swe_calc_ut, swe_fixstar, etc. (8 functions)
- Houses: swe_houses, swe_houses_ex, swe_houses_armc (4 functions)
- Ayanamsa: swe_get_ayanamsa, swe_set_sid_mode (2 functions)
- Ephemeris Files: swe_set_ephe_path, swe_load_ephe, etc. (5 functions)
All 106 Functions
| Category | Functions | Count |
|---|---|---|
| Date & Time Conversions | swe_julday, swe_revjul, swe_date_conversion, swe_utc_time_zone, swe_utc_to_jd, swe_jdet_to_utc, swe_jdut1_to_utc, swe_time_equ, swe_lmt_to_lat, swe_lat_to_lmt | 10 |
| Planet & Body Calculation | swe_calc, swe_calc_ut, swe_calc_pctr, swe_get_planet_name | 4 |
| Fixed Stars | swe_fixstar, swe_fixstar_ut, swe_fixstar_mag, swe_fixstar2, swe_fixstar2_ut, swe_fixstar2_mag | 6 |
| Houses & Angles | swe_houses, swe_houses_ex, swe_houses_ex2, swe_houses_armc, swe_houses_armc_ex2, swe_house_pos, swe_house_name | 7 |
| Sidereal / Ayanamsa | swe_set_sid_mode, swe_get_ayanamsa, swe_get_ayanamsa_ut, swe_get_ayanamsa_ex, swe_get_ayanamsa_ex_ut, swe_get_ayanamsa_name | 6 |
| Legacy Placalc | swe_csnorm, swe_difcsn, swe_difdegn, swe_difcs2n, swe_difdeg2n, swe_difrad2n, swe_csroundsec, swe_d2l, swe_day_of_week, swe_cs2timestr, swe_cs2lonlatstr, swe_cs2degstr | 12 |
| Phenomena | swe_pheno, swe_pheno_ut, swe_azalt, swe_azalt_rev, swe_refrac, swe_refrac_extended, swe_set_lapse_rate | 7 |
| Heliacal Phenomena | swe_heliacal_ut, swe_heliacal_pheno_ut, swe_vis_limit_mag, swe_heliacal_angle, swe_topo_arcus_visionis | 5 |
| Solar Eclipses | swe_sol_eclipse_where, swe_sol_eclipse_how, swe_sol_eclipse_when_loc, swe_sol_eclipse_when_glob | 4 |
| Lunar Eclipses | swe_lun_eclipse_how, swe_lun_eclipse_when, swe_lun_eclipse_when_loc | 3 |
| Lunar Occultations | swe_lun_occult_where, swe_lun_occult_when_loc, swe_lun_occult_when_glob | 3 |
| Nodes & Apsides | swe_nod_aps, swe_nod_aps_ut, swe_get_orbital_elements, swe_orbit_max_min_true_distance | 4 |
| Planetary Crossings | swe_solcross, swe_solcross_ut, swe_mooncross, swe_mooncross_ut, swe_mooncross_node, swe_mooncross_node_ut, swe_helio_cross, swe_helio_cross_ut | 8 |
| Rise / Set / Transit | swe_rise_trans, swe_rise_trans_true_hor | 2 |
| Delta T | swe_deltat, swe_deltat_ex, swe_set_tid_acc, swe_get_tid_acc, swe_set_delta_t_userdef | 5 |
| Sidereal Time | swe_sidtime, swe_sidtime0 | 2 |
| Setup / Configuration | swe_set_ephe_path, swe_set_jpl_file, swe_close, swe_set_topo, swe_version, swe_get_library_path, swe_get_current_file_data | 7 |
| Coordinate Transforms | swe_cotrans, swe_cotrans_sp | 2 |
| Math Utilities | swe_degnorm, swe_radnorm, swe_rad_midp, swe_deg_midp, swe_split_deg | 5 |
| Astro Models | swe_set_astro_models, swe_get_astro_models | 2 |
| Gauquelin | swe_gauquelin_sector | 1 |
| Nutation | swe_set_interpolate_nut | 1 |
| TOTAL | 106 | |
API Reference
Main Class
SwissEphFFI::__construct()
Initialize Swiss Ephemeris FFI
public function __construct(?string $pathToLibrary = null)$pathToLibrary(string|null) β Path to libswe.so/swe.dll. Auto-detected if null.
SwissEphFFI::swe_calc_ut()
Calculate planetary position (Universal Time)
public function swe_calc_ut(float $jd, int $ipl, int $iflag, CData $xx, CData $serr): int
$jd(float) β Julian Day in UT$ipl(int) β Planet constant (SE_SUN, SE_MOON, etc.)$iflag(int) β Calculation flags (SEFLG_SPEED, etc.)$xx(CData) β Output array (double[6])$serr(CData) β Error message buffer (char[256])
int β OK or error codeSwissEphFFI::swe_julday()
Calculate Julian Day number
public function swe_julday(int $year, int $month, int $day, float $hour, int $cal): float
$year,$month,$dayβ Date$hour(float) β Hour (0-24)$cal(int) β Calendar (SE_GREG_CAL or SE_JUL_CAL)
float β Julian DayAll Constants
See the Constants section for complete list.
Constants
Planets
| Constant | Value |
|---|---|
SE_SUN |
0 |
SE_MOON |
1 |
SE_MERCURY |
2 |
SE_VENUS |
3 |
SE_MARS |
4 |
SE_JUPITER |
5 |
SE_SATURN |
6 |
SE_URANUS |
7 |
SE_NEPTUNE |
8 |
SE_PLUTO |
9 |
SE_MEAN_NODE |
10 |
SE_TRUE_NODE |
11 |
SE_MEAN_APOG |
12 |
SE_CHIRON |
15 |
Flags
| Constant | Description |
|---|---|
SEFLG_SPEED |
Calculate speed |
SEFLG_SWIEPH |
Use Swiss Ephemeris files |
SEFLG_MOSEPH |
Use Moshier algorithm |
SEFLG_HELCTR |
Heliocentric calculation |
SEFLG_TRUEPOS |
True position (not mean) |
SEFLG_J2000 |
J2000 equinox |
SEFLG_NONUT |
No nutation |
SEFLG_NOSATEX |
No satellite ephemeris |
Calendar
| Constant | Description |
|---|---|
SE_GREG_CAL |
Gregorian calendar |
SE_JUL_CAL |
Julian calendar |
Ayanamsa
| Constant | Description |
|---|---|
SE_SIDM_LAHIRI |
Lahiri |
SE_SIDM_KRISHNAMURTI |
Krishnamurti |
SE_SIDM_RAMAN |
Raman |
SE_SIDM_YUKTESHWAR |
Yukteshwar |
SE_SIDM_SRIBATTAMS |
Sri Battams |
Understanding Julian Day
What is Julian Day?
Julian Day (JD) is a continuous count of days since the beginning of the Julian Period (January 1, 4713 BC). It's the standard way astronomers represent time for calculations.
Converting to/from JD
// January 1, 2000 at noon UT = 2451545.0
$jd = $sweph->swe_julday(2000, 1, 1, 12.0, SwissEphFFI::SE_GREG_CAL);
// Or use a float directly
$jd = 2451545.0;
$year = $month = $day = $hour = null;
$sweph->swe_jdet_to_date(2451545.0, $year, $month, $day, $hour);
echo "$year-$month-$day $hour";
UT vs TT
- UT (Universal Time) β Civil time, based on Earth's rotation
- TT (Terrestrial Time) β Uniform time, based on atomic clocks
- swe_calc_ut() β Takes JD in UT
- swe_calc() β Takes JD in TT (TT = UT + deltaT)
Coordinates
Geographic Coordinates
Most functions requiring location accept:
- Latitude β Degrees, positive = North, negative = South (-90 to +90)
- Longitude β Degrees, positive = East, negative = West (-180 to +180)
Ecliptic Coordinates
- Longitude β Degrees along ecliptic (0-360Β°)
- Latitude β Degrees above/below ecliptic (-90 to +90)
Equatorial Coordinates
- Right Ascension β Degrees (0-360Β°) from vernal equinox
- Declination β Degrees (-90 to +90) from celestial equator
Common Pitfalls
1. Array Allocation
Always allocate C arrays using FFI, not PHP arrays:
// WRONG - PHP arrays won't work
$xx = [0, 0, 0, 0, 0, 0];
// CORRECT - Allocate with FFI
$xx = $sweph->getFFI()->new("double[6]");
2. Float vs Int for Flags
Always use integers for planet and flag constants, not strings:
// Use constants, not strings
$result = $sweph->swe_calc_ut($jd, SwissEphFFI::SE_SUN, SwissEphFFI::SEFLG_SPEED, $xx, $serr);
3. Julian Day Fraction
The hour in swe_julday() includes the fraction. Noon = 12.0, midnight = 0.0:
// Jan 1, 2000 at midnight UT
$jd = $sweph->swe_julday(2000, 1, 1, 0.0, SwissEphFFI::SE_GREG_CAL);
// Jan 1, 2000 at noon UT
$jd = $sweph->swe_julday(2000, 1, 1, 12.0, SwissEphFFI::SE_GREG_CAL);
// Jan 1, 2000 at 6:00 AM UT
$jd = $sweph->swe_julday(2000, 1, 1, 6.0, SwissEphFFI::SE_GREG_CAL);
Performance
Tips for Optimal Performance
- Reuse the instance β Create SwissEphFFI once, reuse for all calculations.
- Batch calculations β If calculating multiple planets, use swe_calc() in a loop.
- Skip unnecessary flags β Only use SEFLG_SPEED if you need velocity.
- Use Moshier when possible β Faster than loading ephemeris files.
Benchmark
Typical performance on modern hardware:
- Single planet calculation: ~0.1ms
- Full chart (10 planets): ~1ms
- With ephemeris files: ~5-10ms (disk I/O)
Comparison with Other Packages
| Feature | swiss-ephemeris-ffi | php-sweph | swephe-php |
|---|---|---|---|
| Function Coverage | 106 (100%) | 100 (94%) | 2 (2%) |
| FFI Support | Yes | No | No |
| Built-in Binary | Yes | No | No |
| Composer Package | Yes | No | No |
| Laravel Support | Yes | No | No |
| Last Updated | Active | 2021 | 2019 |
Laravel Integration
This package includes a Laravel Service Provider and Facade for convenient access.
Configuration
// config/app.php (for Laravel < 11)
'providers' => [
SwissEph\Service\SwissEphServiceProvider::class,
],
'aliases' => [
'SwissEph' => SwissEph\Service\SwissEphFacade::class,
],
Usage
use SwissEph\Service\SwissEphFacade;
// Calculate Julian Day
$jd = SwissEph::swe_julday(2000, 1, 1, 12.0, SwissEph::SE_GREG_CAL);
// Get planet position
$xx = SwissEph::swe_calc_ut($jd, SwissEph::SE_SUN, SwissEph::SEFLG_SPEED);
Testing
Run the test suite to verify the package is working correctly.
# Run all tests
composer test
# Check code quality
composer quality
Troubleshooting
FFI Extension Not Loaded
Ensure both lines are present in your php.ini:
extension=ffi
ffi.enable=1
Library Not Found
Linux: Add library path to ldconfig:
echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/swisseph.conf
sudo ldconfig
macOS:
export DYLD_LIBRARY_PATH=/usr/local/lib:$DYLD_LIBRARY_PATH
Windows:
# Add to PATH
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\php\ext", "Machine")
License
My Package
My package is MIT Licensed. You're free to use it in commercial and personal projects.
Swiss Ephemeris Itself
The underlying Swiss Ephemeris C library has its own licensing:
- Free Edition (AGPL-3.0) β Can be redistributed with attribution
- Commercial License β Available from Astrodienst for proprietary software
For details, see astro.com/swisseph.
Support
Getting Help
- GitHub Issues: Report bugs
- GitHub Discussions: Q&A
- Email: jayeshmepani777@gmail.com