PHPackages                             as-cornell/as\_people\_ldap - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. as-cornell/as\_people\_ldap

ActiveDrupal-custom-module[Utility &amp; Helpers](/categories/utility)

as-cornell/as\_people\_ldap
===========================

Displays LDAP people data from directory.cornell.edu by NetID in a block.

v2.0.1(2mo ago)0884↓50%GPL-3.0-or-laterPHP

Since Oct 14Pushed 2mo ago3 watchersCompare

[ Source](https://github.com/as-cornell/as_people_ldap)[ Packagist](https://packagist.org/packages/as-cornell/as_people_ldap)[ Docs](https://communications.as.cornell.edu)[ RSS](/packages/as-cornell-as-people-ldap/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)DependenciesVersions (10)Used By (0)

[![Latest Stable Version](https://camo.githubusercontent.com/c1499e4a440c9d5fbc352cd8025de7c7b6cddef72a5bc0bf4dd127425ddc1b63/68747470733a2f2f706f7365722e707567782e6f72672f61732d636f726e656c6c2f61735f70656f706c655f6c6461702f76)](https://packagist.org/packages/as-cornell/as_people_ldap)

AS People LDAP (as\_people\_ldap)
=================================

[](#as-people-ldap-as_people_ldap)

Provides Cornell LDAP directory integration for Drupal 10 sites.

Table of Contents
-----------------

[](#table-of-contents)

- [Introduction](#introduction)
- [Architecture](#architecture)
- [Services](#services)
- [Usage](#usage)
- [Blocks](#blocks)
- [Routes](#routes)
- [Requirements](#requirements)
- [Installation](#installation)
- [Configuration](#configuration)
- [SSL/TLS Certificate Setup](#ssltls-certificate-setup)
- [Troubleshooting](#troubleshooting)
- [Maintainers](#maintainers)

Introduction
------------

[](#introduction)

The AS People LDAP module fetches and displays people data from Cornell's LDAP directory (directory.cornell.edu) by NetID. It provides:

- **Service-based architecture** with dependency injection
- **Automatic caching** (4-6 day random cache TTL)
- **Block plugin** for displaying LDAP data
- **Controller** with test route for NetID lookups
- **Secure LDAPS** connection with certificate support
- **Flexible formatting** utilities for LDAP data

Architecture
------------

[](#architecture)

The module uses an **object-oriented architecture** with separate service classes:

```
as_people_ldap/
├── src/
│   ├── Service/
│   │   ├── LdapApiService.php         # LDAP connections and caching
│   │   └── LdapFormatterService.php   # Data formatting utilities
│   ├── Controller/
│   │   └── PeopleLdapController.php   # Route controller
│   ├── Plugin/Block/
│   │   └── ASLdap.php                 # LDAP block
│   └── Form/
│       └── AsPeopleLdapSettingsForm.php # Settings form
├── as_people_ldap.services.yml        # Service definitions
└── as_people_ldap.module              # Hooks only

```

**Key Benefits:**

- ✅ Testable service classes
- ✅ Reusable via dependency injection
- ✅ Clear separation of concerns
- ✅ Follows Drupal 10 best practices

Services
--------

[](#services)

### LdapApiService

[](#ldapapiservice)

Handles LDAP communication with Cornell directory and manages caching.

**Service ID:** `as_people_ldap.api`

**Dependencies:**

- `@cache.data` - Cache backend
- `@config.factory` - Config factory
- `@logger.channel.as_people_ldap` - Logger channel

**Methods:**

#### `getNetIdLdap($netid)`

[](#getnetidldapnetid)

Fetches LDAP data for a given Cornell NetID with automatic caching.

**Parameters:**

- `$netid` (string) - The Cornell NetID to look up

**Returns:** `array` - Array of LDAP data or empty array if not found

**Example:**

```
$ldap_api = \Drupal::service('as_people_ldap.api');
$data = $ldap_api->getNetIdLdap('abc123');
```

**LDAP Query Details:**

- **Host:** `ldaps://query.directory.cornell.edu:636/`
- **Base DN:** `ou=People,o=Cornell University,c=US`
- **Filter:** `(uid={netid})`
- **Attributes:** `cn`, `cornelleducampusaddress`, `cornelledupublishedemail`, `cornelleducampusphone`

**Caching:**

- Cache ID: `as_people_ldap:{netid}`
- Cache Duration: Random 4-6 days (345600-518400 seconds)
- Cache Bin: `cache.data`
- Only caches if valid Cornell email found

**Debug Mode:**

- Enabled on `lando` and `dev` environments
- Shows connection, bind, and search details
- Displays TLS certificate information

### LdapFormatterService

[](#ldapformatterservice)

Provides utilities for formatting LDAP data.

**Service ID:** `as_people_ldap.formatter`

**Methods:**

#### `formatLdapDataAsMarkup(array $ldap_data)`

[](#formatldapdataasmarkuparray-ldap_data)

Formats LDAP data as HTML markup.

**Parameters:**

- `$ldap_data` (array) - LDAP data array from Cornell directory

**Returns:** `string` - HTML markup for display

**Example:**

```
$formatter = \Drupal::service('as_people_ldap.formatter');
$markup = $formatter->formatLdapDataAsMarkup($ldap_data);
```

**Generated Markup Includes:**

- Campus address
- Email (as mailto link)
- Phone number

#### `getLdapField(array $ldap_data, $field, $index = 0)`

[](#getldapfieldarray-ldap_data-field-index--0)

Gets a specific field value from LDAP data.

**Parameters:**

- `$ldap_data` (array) - LDAP data array
- `$field` (string) - The LDAP field name to retrieve
- `$index` (int) - The index of the value (default: 0)

**Returns:** `string|null` - The field value or NULL if not found

#### `getCampusAddress(array $ldap_data)`

[](#getcampusaddressarray-ldap_data)

Gets the campus address from LDAP data.

**Returns:** `string|null`

#### `getEmail(array $ldap_data)`

[](#getemailarray-ldap_data)

Gets the email address from LDAP data.

**Returns:** `string|null`

#### `getPhone(array $ldap_data)`

[](#getphonearray-ldap_data)

Gets the campus phone number from LDAP data.

**Returns:** `string|null`

#### `getCommonName(array $ldap_data)`

[](#getcommonnamearray-ldap_data)

Gets the common name (cn) from LDAP data.

**Returns:** `string|null`

Usage
-----

[](#usage)

### Using Services in Custom Code

[](#using-services-in-custom-code)

**In a Controller:**

```
namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\as_people_ldap\Service\LdapApiService;
use Drupal\as_people_ldap\Service\LdapFormatterService;
use Symfony\Component\DependencyInjection\ContainerInterface;

class MyController extends ControllerBase {

  protected $ldapApi;
  protected $ldapFormatter;

  public function __construct(LdapApiService $ldap_api, LdapFormatterService $ldap_formatter) {
    $this->ldapApi = $ldap_api;
    $this->ldapFormatter = $ldap_formatter;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('as_people_ldap.api'),
      $container->get('as_people_ldap.formatter')
    );
  }

  public function myPage($netid) {
    $data = $this->ldapApi->getNetIdLdap($netid);
    $email = $this->ldapFormatter->getEmail($data);
    // Use data...
  }
}
```

**In a Service:**

```
# my_module.services.yml
services:
  my_module.my_service:
    class: Drupal\my_module\MyService
    arguments: ['@as_people_ldap.api', '@as_people_ldap.formatter']
```

### Accessing Individual Fields

[](#accessing-individual-fields)

```
$ldap_api = \Drupal::service('as_people_ldap.api');
$formatter = \Drupal::service('as_people_ldap.formatter');

$data = $ldap_api->getNetIdLdap('abc123');

$name = $formatter->getCommonName($data);
$email = $formatter->getEmail($data);
$phone = $formatter->getPhone($data);
$address = $formatter->getCampusAddress($data);
```

Blocks
------

[](#blocks)

### LDAP Block

[](#ldap-block)

**Block ID:** `ldap_block`**Admin Label:** "LDAP Block" **Category:** "People"

Displays LDAP data for a configured NetID.

**Configuration:**

- `netid` - Cornell NetID to display

**Usage:**

1. Navigate to `/admin/structure/block`
2. Click "Place block"
3. Search for "LDAP Block"
4. Configure NetID
5. Save

Routes
------

[](#routes)

### Test Route

[](#test-route)

**Route:** `/people_ldap/{netid}`

Displays LDAP data for a given NetID in a test page.

**Example:**

- `/people_ldap/abc123`

**Output:** HTML page with LDAP data including address, email, and phone.

Requirements
------------

[](#requirements)

- Drupal: &gt;= 10.0
- PHP: &gt;= 8.1
- PHP LDAP extension
- Access to Cornell LDAP directory (ldaps://query.directory.cornell.edu:636/)
- Valid LDAP credentials (bind RDN and password)
- SSL/TLS certificates for LDAPS connection

**Drupal Modules:**

- [Key](https://www.drupal.org/project/key) - Required for secure credential storage

Installation
------------

[](#installation)

### Via Composer (Recommended)

[](#via-composer-recommended)

```
# Install the module and Key module dependency
composer require as-cornell/as_people_ldap drupal/key
drush en key as_people_ldap -y
drush cr
```

### Manual Installation

[](#manual-installation)

1. Download the module to `/modules/custom/as_people_ldap`
2. Enable the module: `drush en as_people_ldap -y`
3. Clear cache: `drush cr`

Configuration
-------------

[](#configuration)

### Module Settings

[](#module-settings)

Navigate to: `/admin/config/services/as-people-ldap-settings`

**Required Settings:**

- **LDAP RDN or DN** - Full distinguished name for LDAP bind (e.g., `uid=myapp,ou=apps,o=Cornell University,c=US`)
- **LDAP Password** - Password for LDAP bind

**Optional Settings:**

- **Enable debug mode** - Checkbox to enable verbose debugging output

**Example Configuration:**

```
LDAP RDN: uid=drupal-ldap,ou=applications,o=Cornell University,c=US
LDAP Password: [secure password]
Debug Mode: ☐ (unchecked for production)

```

### Secure Credential Storage

[](#secure-credential-storage)

Credentials are stored securely using the **Key module**:

✅ **Stored in database** - Keys are saved to the database via the Key module ✅ **Never exported** - Credentials are NOT included in configuration exports ✅ **Separate from config** - Not stored in `as_people_ldap.settings.yml`✅ **Password field security** - Password field is type `password` (masked input)

**How it works:**

1. When you save the settings form, credentials are stored as Key entities:
    - `as_people_ldap_rdn` - Stores the LDAP RDN/DN
    - `as_people_ldap_password` - Stores the LDAP password
2. Keys are managed by the Key module and stored in the database
3. The LdapApiService retrieves credentials from the Key repository at runtime
4. Configuration exports (`drush cex`) do NOT export these keys

**Password updates:**

- Leave the password field blank to keep the existing password
- Enter a new password to update the stored password
- A status message confirms when a password is currently stored

**Viewing stored keys:**

```
# List all keys
drush key:list

# View key details (will show metadata but not the actual value)
drush config:get key.key.as_people_ldap_rdn
drush config:get key.key.as_people_ldap_password
```

**Important:** While keys are stored in the database, you should still use environment-specific databases and never commit database dumps containing production credentials to version control.

### Debug Mode

[](#debug-mode)

Debug mode provides verbose output for troubleshooting LDAP connections and queries.

**When debug mode is enabled, the following information is displayed:**

- LDAP connection attempts and results
- Bind authentication attempts
- Search queries with filters and attributes
- TLS/SSL certificate information (LDAPTLS\_CACERT, LDAPTLS\_CERT, etc.)
- Cache hits vs. fresh LDAP queries
- Full LDAP response data

**Requirements for debug mode:**

Debug mode requires **BOTH** of these conditions to be true:

1. Environment must be `lando` (local) OR `dev` (Pantheon dev)
2. **AND** the "Enable debug mode" checkbox must be checked in settings

**To enable debug mode:**

1. **Via Settings Form:**

    - Navigate to `/admin/config/services/as-people-ldap-settings`
    - Check "Enable debug mode"
    - Save configuration
    - **Note:** Debug output will only appear in lando/dev environments
2. **Via Drush:**

    ```
    drush config:set as_people_ldap.settings debug_mode 1 -y
    drush cr
    ```

**Security:** Debug mode will **NOT** work in production environments (test, live), even if the checkbox is enabled. This prevents accidental exposure of sensitive information in production.

**To disable debug mode:**

```
drush config:set as_people_ldap.settings debug_mode 0 -y
drush cr
```

### Configuration via Drush

[](#configuration-via-drush)

**Note:** With Key module integration, credentials are stored as keys, not in configuration. Use the settings form for credential management, or manage keys directly:

```
# Enable/disable debug mode (this IS stored in config)
drush config:set as_people_ldap.settings debug_mode 1 -y  # Enable
drush config:set as_people_ldap.settings debug_mode 0 -y  # Disable

# View stored keys (metadata only, not values)
drush key:list

# Clear cache after changes
drush cr
```

**Managing credentials programmatically** (advanced):

If you need to set credentials via Drush for automation, you would need to create the key entities directly. It's recommended to use the settings form instead.

### Cache Settings

[](#cache-settings)

LDAP data is cached for a **random 4-6 days** in the `cache.data` bin.

**To clear LDAP cache:**

```
# Clear all cache
drush cr

# Or manually clear specific cache ID
drush php-eval "\Drupal::cache('data')->delete('as_people_ldap:abc123');"
```

### Logging

[](#logging)

The module provides its own logger channel: `logger.channel.as_people_ldap`

**View logs:**

```
drush watchdog:show --type=as_people_ldap
```

SSL/TLS Certificate Setup
-------------------------

[](#ssltls-certificate-setup)

Cornell's LDAP directory requires LDAPS (LDAP over SSL/TLS) with client certificates.

### Certificate Files Required

[](#certificate-files-required)

Place the following files in `/sites/default/files/private/certs/`:

- `ca.crt` - CA Certificate
- `client.crt` - Client Certificate
- `client.pem` - Client Private Key (without password)

### Settings.php Configuration

[](#settingsphp-configuration)

Add the following to your `settings.php`:

```
// LDAP - Specify file that contains the TLS CA Certificate.
// Can also be used to provide intermediate certificate to trust remote servers.
$tls_cacert = DRUPAL_ROOT . '/sites/default/files/private/certs/ca.crt';
if (!file_exists($tls_cacert)) {
  die($tls_cacert . ' CA cert does not exist');
}
putenv("LDAPTLS_CACERT=$tls_cacert");

// LDAP - Specify file that contains the client certificate.
$tls_cert = DRUPAL_ROOT . '/sites/default/files/private/certs/client.crt';
if (!file_exists($tls_cert)) {
  die($tls_cert . ' client cert does not exist');
}
putenv("LDAPTLS_CERT=$tls_cert");

// LDAP - Specify file that contains private key w/o password for TLS_CERT.
$tls_key = DRUPAL_ROOT . '/sites/default/files/private/certs/client.pem';
if (!file_exists($tls_key)) {
  die($tls_key . ' client key does not exist');
}
putenv("LDAPTLS_KEY=$tls_key");

// LDAP - Specify cert directory.
$tls_cert_dir = DRUPAL_ROOT . '/sites/default/files/private/certs/';
putenv("LDAPTLS_CACERTDIR=$tls_cert_dir");

// LDAP - Allow server certificate check in a TLS session.
putenv('LDAPTLS_REQCERT=allow');
```

### Deploying Certificates via SFTP

[](#deploying-certificates-via-sftp)

```
# Connect to server via SFTP
sftp user@server

# Navigate to private files directory
cd web/sites/default/files/private

# Upload certificates directory
put -r certs certs

# Exit
exit
```

### Verify Certificate Setup

[](#verify-certificate-setup)

```
# Check environment variables
drush php-eval "
echo 'LDAPTLS_CACERT: ' . getenv('LDAPTLS_CACERT') . PHP_EOL;
echo 'LDAPTLS_CERT: ' . getenv('LDAPTLS_CERT') . PHP_EOL;
echo 'LDAPTLS_KEY: ' . getenv('LDAPTLS_KEY') . PHP_EOL;
echo 'LDAPTLS_CACERTDIR: ' . getenv('LDAPTLS_CACERTDIR') . PHP_EOL;
echo 'LDAPTLS_REQCERT: ' . getenv('LDAPTLS_REQCERT') . PHP_EOL;
"

# Check if files exist
ls -l web/sites/default/files/private/certs/
```

Troubleshooting
---------------

[](#troubleshooting)

### LDAP Connection Issues

[](#ldap-connection-issues)

1. **Check PHP LDAP extension:**

    ```
    php -m | grep ldap
    ```
2. **Check credentials:**

    ```
    drush config:get as_people_ldap.settings
    ```
3. **Test LDAP connection:**

    ```
    drush php-eval "
    \$api = \Drupal::service('as_people_ldap.api');
    \$data = \$api->getNetIdLdap('abc123');
    print_r(\$data);
    "
    ```
4. **Check logs:**

    ```
    drush watchdog:show --type=as_people_ldap --count=20
    ```

### Certificate Issues

[](#certificate-issues)

**Error: "CA cert does not exist"**

- Verify certificate files are in `/sites/default/files/private/certs/`
- Check file permissions (should be readable by web server)
- Verify path in `settings.php` is correct

**Error: "Unable to connect"**

- Check firewall allows outbound LDAPS (port 636)
- Verify server can reach `query.directory.cornell.edu`
- Check certificate validity dates

**Error: "Unable to bind"**

- Verify LDAP RDN is correct full distinguished name
- Check LDAP password is correct
- Ensure credentials have permission to query directory

### Cache Issues

[](#cache-issues)

**Stale data displayed:**

```
# Clear specific NetID cache
drush php-eval "\Drupal::cache('data')->delete('as_people_ldap:abc123');"

# Clear all LDAP cache
drush php-eval "
\$cache = \Drupal::cache('data');
\$cache->deleteMultiple(\$cache->getMultiple(['as_people_ldap:']));
"
```

### Enabling Debug Output

[](#enabling-debug-output)

Debug information requires **BOTH** conditions to be met:

1. Running in `lando` or `dev` environment
2. Debug mode checkbox is enabled in settings

**To enable debug output:**

1. **Via Settings Form (Recommended):**

    - Navigate to `/admin/config/services/as-people-ldap-settings`
    - Check "Enable debug mode"
    - Save configuration
    - Clear cache: `drush cr`
    - **Important:** Debug will only show output in lando/dev environments
2. **Via Drush:**

    ```
    drush config:set as_people_ldap.settings debug_mode 1 -y
    drush cr
    ```

**Debug output includes:**

- Connection attempts to LDAP server
- Bind authentication results
- Search query details (base DN, filter, attributes)
- TLS/SSL certificate paths and hashes
- Cache hit/miss information
- Full LDAP response data

**Security Feature:** Debug mode will NOT work in production environments (test, live), even if enabled in settings. This prevents accidental exposure of sensitive information:

- LDAP server details
- Certificate paths
- Query filters
- Full LDAP responses with personal data

LDAP Data Structure
-------------------

[](#ldap-data-structure)

### Example LDAP Response

[](#example-ldap-response)

```
[
  0 => [
    'cn' => [
      0 => 'John Doe',
      'count' => 1
    ],
    'cornelleducampusaddress' => [
      0 => '123 Day Hall, Ithaca, NY 14853',
      'count' => 1
    ],
    'cornelledupublishedemail' => [
      0 => 'jd123@cornell.edu',
      'count' => 1
    ],
    'cornelleducampusphone' => [
      0 => '607-255-1234',
      'count' => 1
    ],
    'count' => 4
  ],
  'count' => 1
]
```

Development
-----------

[](#development)

### Running Tests

[](#running-tests)

```
# PHPUnit tests (if implemented)
vendor/bin/phpunit modules/custom/as_people_ldap

# Code standards
vendor/bin/phpcs --standard=Drupal modules/custom/as_people_ldap
```

### Contributing

[](#contributing)

1. Follow Drupal coding standards
2. Add PHPUnit tests for new functionality
3. Update this README for new features
4. Use semantic versioning for releases

Upgrading from Previous Versions
--------------------------------

[](#upgrading-from-previous-versions)

If you're upgrading from a version that stored credentials in configuration:

1. **Install Key module:**

    ```
    composer require drupal/key
    drush en key -y
    ```
2. **Re-enter credentials:**

    - Navigate to `/admin/config/services/as-people-ldap-settings`
    - The form will be empty (old config values are no longer used)
    - Enter your LDAP RDN and password
    - Save the form
3. **Verify credentials are stored as keys:**

    ```
    drush key:list | grep as_people_ldap
    ```

    You should see:

    - `as_people_ldap_rdn`
    - `as_people_ldap_password`
4. **Test LDAP connection:**

    - Visit `/people_ldap/{netid}` with a valid NetID
    - Or use a block configured with a NetID
5. **Remove old config (optional):**

    ```
    # Export config to remove old ldaprdn/ldappass values
    drush config:delete as_people_ldap.settings.ldaprdn
    drush config:delete as_people_ldap.settings.ldappass
    drush cex -y
    ```

Security Considerations
-----------------------

[](#security-considerations)

- ✅ LDAP credentials stored using Key module (database, not config)
- ✅ Credentials never exported with configuration
- ✅ LDAPS (SSL/TLS) used for all connections
- ✅ Client certificates required for authentication
- ✅ Passwords never displayed in debug output
- ✅ Password field uses masked input
- ✅ Only caches data with valid Cornell emails
- ✅ Input sanitization on NetID parameter
- ✅ Debug mode only works in lando/dev environments

**Production Recommendations:**

- Restrict access to settings form (`/admin/config/services/as-people-ldap-settings`)
- Use environment-specific databases (don't copy production DB to dev)
- Monitor LDAP query logs
- Rotate certificates regularly
- Never commit database dumps with production credentials

Maintainers
-----------

[](#maintainers)

Current maintainers for Drupal 10:

- Mark Wilson (markewilson)

License
-------

[](#license)

GPL-2.0-or-later

Changelog
---------

[](#changelog)

### 2.1.0 (2025-02-18)

[](#210-2025-02-18)

- **Security:** Integrated Key module for secure credential storage
- Credentials now stored in database, never exported with config
- Password field changed to masked input
- Added debug mode checkbox (only works in lando/dev)
- Improved form UX with fieldsets and better descriptions
- Updated README with Key module documentation

### 2.0.0 (2025-02-18)

[](#200-2025-02-18)

- Refactored to OOP architecture with service classes
- Added dependency injection throughout
- Improved error handling and logging
- Enhanced documentation

### 1.x

[](#1x)

- Initial procedural implementation
- Basic LDAP directory integration

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance84

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~61 days

Recently: every ~47 days

Total

9

Last Release

83d ago

Major Versions

v1.0.8 → v2.0.12026-02-18

### Community

Maintainers

![](https://www.gravatar.com/avatar/f17dcdbbef5d086a5058ec138bc2f825149a5711e83709868f43494eef805b51?d=identicon)[markewilson](/maintainers/markewilson)

---

Top Contributors

[![markewilson](https://avatars.githubusercontent.com/u/7050965?v=4)](https://github.com/markewilson "markewilson (20 commits)")

### Embed Badge

![Health badge](/badges/as-cornell-as-people-ldap/health.svg)

```
[![Health](https://phpackages.com/badges/as-cornell-as-people-ldap/health.svg)](https://phpackages.com/packages/as-cornell-as-people-ldap)
```

###  Alternatives

[sanskrit/sanscript

Transliteration library for Sanskrit and other Indian languages.

162.9k](/packages/sanskrit-sanscript)[wc-develop/php-ofx-reader

Template-based OFX reader

101.7k](/packages/wc-develop-php-ofx-reader)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
