phil / contact
Contact cards for PHP: a person or an organisation with a structured name ordered and sorted by its culture, emails, typed phone lines, web addresses and messaging handles, written as vCard 4.0, schema.org JSON-LD and JSContact, read from JSContact, and kept as an array for one JSON column
Requires
- php: >=8.5
- ext-filter: *
- ext-intl: *
- ext-mbstring: *
- phil/address: ^0.1@dev
- phil/enums: ^0.3@dev
- phil/instant-messenger: ^0.1@dev
- phil/phone: ^0.1@dev
- phil/url: ^0.1@dev
Requires (Dev)
- phpunit/phpunit: ^13.4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-12 23:52:53 UTC
README
A contact card for a person or an organisation, ready to save as vCard or JSON-LD.
phil/contact holds a contact card as an immutable value object. A card names a person, who may work for an
organisation, or an organisation alone. It carries a title, email addresses, phone lines each with its role (voice,
mobile, fax, text or pager), web addresses and messaging handles. A person's name may be kept in its parts — given
name, particle, family name and the rest — and is then written and sorted as its culture does: Charles de Gaulle
sorts under Gaulle in French, Bartók Béla is written family name first in Hungarian. The card is written as a
vCard 4.0 file, as RFC 6350 describes it, as schema.org JSON-LD, a Person or an Organization, and as JSContact
(RFC 9553), which it also reads, strictly and within bounds. It also turns into an array of texts for one JSON column.
It does not hold postal addresses, which belong to phil/address and travel beside the card in JSContact, nor opening
hours, which belong to phil/opening-hours, nor a contact form, which phil/contact-form-bundle gives.
It is for any application that shows people how to reach someone: a directory entry, a shop's page, an organiser's
profile. Phone numbers, web addresses and handles are no plain strings. They are the value types of phil/phone,
phil/url and phil/instant-messenger, so a card only holds what those packages accept.
It is a plain PHP library, usable in any PHP code. In a Symfony application, reach for phil/contact-bundle, which
adds the form, the Doctrine column type, vCard downloads and Twig functions.
The design keeps the card apart from its formats. ContactCard and its parts hold checked values only, and VCard,
SchemaOrg, JSContactWriter and ContactCardArray each write the card in one format. The naming conventions of
cultures are typed values, NameConvention, that an application may replace. Reading a stored card back throws
UnreadableContact for anything ContactCardArray does not write; reading JSContact keeps what the card does not map
and reports what it reads without keeping, never dropping anything silently.
Status
| Part | State |
|---|---|
ContactCard, Email, PhoneLine and PhoneRole |
Implemented and tested |
PersonName, NameConvention, NameConventions: names written and sorted by culture |
Implemented and tested |
VCard: vCard 4.0, escaped and folded |
Implemented and tested |
SchemaOrg: Person or Organization JSON-LD |
Implemented and tested |
ContactCardArray: to and from an array, for storage |
Implemented and tested |
JSContactWriter, JSContactReader: JSContact 1.0, bounded and fuzzed |
Implemented and tested |
Documentation
| Document | What it covers |
|---|---|
| Installation | Requirements and installing. |
| Cards | ContactCard, emails, phone lines and their roles. |
| Person names | PersonName, its parts, and the conventions of cultures. |
| vCard and schema.org | The vCard and JSON-LD exports. |
| JSContact | Writing and reading JSContact, the mapping, the notices and the bounds. |
| Storage | ContactCardArray, what is checked when a card is read back, and encrypting stored cards with #[Encrypted] and #[BlindIndexed]. |
| Extension points | The ports, arguments and data an application chooses or replaces. |
| Testing | The library's tests and mutation bar. |
| Reference | Every type of the library, with its role. |
Quick start
composer require phil/contact
use Phil\Contact\ContactCard; use Phil\Contact\Email; use Phil\Contact\PersonName; use Phil\Contact\PhoneLine; use Phil\Contact\PhoneRole; use Phil\Contact\VCard; use Phil\Phone\PhoneNumber; $card = ContactCard::of( organization: 'Analytical Engines', emails: [Email::of('ada@example.com')], phones: [new PhoneLine(PhoneRole::Mobile, PhoneNumber::parse('+1 514 555 0123'))], structuredName: PersonName::of(given: 'Ada', family: 'Lovelace', culture: 'en-GB'), ); $card->name; // "Ada Lovelace" $card->structuredName->sortKey(); // "Lovelace, Ada" VCard::of($card); // "BEGIN:VCARD\r\nVERSION:4.0\r\nKIND:individual\r\nFN:Ada Lovelace\r\n…"
Requirements
PHP 8.5 or later with ext-filter, ext-intl and ext-mbstring; phil/phone, phil/url,
phil/instant-messenger and phil/address 0.1, and phil/enums 0.3.
Contributing
Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.
License
MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.