{"id":13760121,"url":"https://github.com/alwaysblank/schemer","last_synced_at":"2025-06-27T07:34:47.807Z","repository":{"id":56946575,"uuid":"199539648","full_name":"alwaysblank/schemer","owner":"alwaysblank","description":"Help with schema.org generation.","archived":false,"fork":false,"pushed_at":"2020-04-27T20:27:56.000Z","size":104,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-15T00:39:27.253Z","etag":null,"topics":["helper","microdata","php","php7","schema"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alwaysblank.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-29T23:25:41.000Z","updated_at":"2025-06-07T12:17:26.000Z","dependencies_parsed_at":"2022-08-21T07:20:24.569Z","dependency_job_id":null,"html_url":"https://github.com/alwaysblank/schemer","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/alwaysblank/schemer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alwaysblank%2Fschemer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alwaysblank%2Fschemer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alwaysblank%2Fschemer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alwaysblank%2Fschemer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alwaysblank","download_url":"https://codeload.github.com/alwaysblank/schemer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alwaysblank%2Fschemer/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262213035,"owners_count":23276036,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["helper","microdata","php","php7","schema"],"created_at":"2024-08-03T13:01:03.630Z","updated_at":"2025-06-27T07:34:47.785Z","avatar_url":"https://github.com/alwaysblank.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"# Schemer 🦹\r\n\r\nHelp with [schema.org](https://schema.org/) markup generation.\r\n\r\n[![Build Status](https://travis-ci.org/alwaysblank/schemer.svg?branch=master)](https://travis-ci.org/alwaysblank/schemer)\r\n\r\n## Usage\r\n\r\n\u003e **Note:** Currently, Schemer generates only the \"microdata\" format.\r\n\r\nSchemer is designed to be pretty simple to use: Just pass a structured array to the `::build()` static method on the Scheme you want:\r\n\r\n```php\r\necho AlwaysBlank\\Schemer\\Scheme\\PostalAddress::build([\r\n    ['street', '123 Oak St'],\r\n    ['state', 'OR'],\r\n    ['city', 'Portland'],\r\n    ['zip', '97123'],\r\n    ['pobox', 'P.O. 1234'],\r\n    ['country', 'USA'],\r\n]);\r\n\r\n// \u003cspan itemscope itemprop=\"http://schema.org/PostalAddress\"\u003e\u003cspan itemprop=\"streetAddress\"\u003e123 Oak St\u003c/span\u003e\u003cspan itemprop=\"addressRegion\"\u003eOR\u003c/span\u003e\u003cspan itemprop=\"addressLocality\"\u003ePortland\u003c/span\u003e\u003cspan itemprop=\"postalCode\"\u003e97123\u003c/span\u003e\u003cspan itemprop=\"postOfficeBoxNumber\"\u003eP.O. 1234\u003c/span\u003e\u003cspan itemprop=\"addressCountry\"\u003eUSA\u003c/span\u003e\u003c/span\u003e\r\n```\r\n\r\nYou can optionally pass a second argument, which is a array of arguments that will override the defaults. This allows you to do things like change the wrapping element tag, add arbitrary attributes, etc.\r\n\r\n```php\r\necho AlwaysBlank\\Schemer\\Scheme\\PostalAddress::build([\r\n    ['street', '123 Oak St'],\r\n    ['state', 'OR'],\r\n    ['city', 'Portland'],\r\n    ['zip', '97123'],\r\n    ['pobox', 'P.O. 1234'],\r\n    ['country', 'USA'],\r\n], [\r\n    'tag' =\u003e 'a',\r\n    'attributes' =\u003e [\r\n        'href'      =\u003e 'https://www.alwaysblank.org',\r\n        'hidden'    =\u003e true,\r\n    ],\r\n]);\r\n// \u003ca itemscope itemprop=\"http://schema.org/PostalAddress\" href=\"https://www.alwaysblank.org' hidden\u003e\u003cspan itemprop=\"streetAddress\"\u003e123 Oak St\u003c/span\u003e\u003cspan itemprop=\"addressRegion\"\u003eOR\u003c/span\u003e\u003cspan itemprop=\"addressLocality\"\u003ePortland\u003c/span\u003e\u003cspan itemprop=\"postalCode\"\u003e97123\u003c/span\u003e\u003cspan itemprop=\"postOfficeBoxNumber\"\u003eP.O. 1234\u003c/span\u003e\u003cspan itemprop=\"addressCountry\"\u003eUSA\u003c/span\u003e\u003c/a\u003e\r\n```\r\n\r\n\u003e The actual HTML output is slightly different: To allow for simple breaks but maximum styling options, each inline element is followed by `\u003cspan class=\"spc\"\u003e\u0026#8203;\u003c/span\u003e`, a zero-width space. This allows the browser to break at this point, but contributes no formatting apart from that.\r\n\r\n### Schemes\r\n\r\nCurrently Schemer supports the following Schemes in some fashion. More will be added in the future! Please feel free to file an issue with schemes you'd like to see, or—even better!—a pull request adding them. See \"How It Works\" below for more information on how to create new Schemes and Properties.\r\n\r\n- [`PostalAddress`](https://schema.org/PostalAddress) can understand the following:\r\n    - `street`\r\n    - `state`\r\n    - `city`\r\n    - `zip` (zip code)\r\n    - `pobox` (P.O. box)\r\n    - `country`\r\n- [`LocalBusiness`](https://schema.org/LocalBusiness) can understand the following:\r\n    - `name`\r\n    - `phone` (phone segments will be rendered as links with `tel:`)\r\n    - `url` (url can take a string, or an array with two items keyed `url` and `content`)\r\n    - `address` (this is an array containing keys that will be understood by `PostalAddress`)\r\n    \r\n## How It Works\r\n\r\nThere are three basic parts to Schemer:\r\n\r\n- `Node`\r\n- `Property`\r\n- `Scheme`\r\n\r\n### Node\r\n\r\nThe `Node` is the simplest element, and also the only element that actually behaves like a class (`Property`s and `Scheme`s, though classes, are really just wrappers for static methods). It provides a sort of wrapper for individual segments of a `Propery` or `Scheme`, and is what is used to generate the actual HTML.\r\n\r\nIn general, you probably won't be interacting with `Node`s very much outside of creating them by passing a set of arguments:\r\n\r\n```php\r\n$Node = Node::add([\r\n    'itemscope'     =\u003e true,\r\n    'itemtype'      =\u003e 'http://schema.org/LocalBusiness',\r\n    'itemprop'      =\u003e 'description',\r\n    'tag'           =\u003e 'div',\r\n    'content'       =\u003e \"Always Blank\",\r\n    'attributes'    =\u003e [\r\n        'href'      =\u003e 'https://www.alwaysblank.org',\r\n        'hidden'    =\u003e true,\r\n    ]\r\n]);\r\n```\r\n\r\nUnderstanding this syntax is helpful, because it will be necessary for creating new `Property`s, or to modify existing properties or `Scheme`s.\r\n\r\n### Property\r\n\r\nA `Property`'s one purpose in life is to return a Node with the appropriate arguments for the the schema.org property it represents. It is also implemented as a **Trait** so that it can be composed into a `Scheme` later on.\r\n\r\nA `Property` must meet the following requirements:\r\n\r\n- It implements a public static method that has the same name as whatever you want to use as the key/name for the field in question—i.e. `address` or `phone`. In general, these names should be *simple* rather than directly matching the schema.org property they represent. For instance, `telephone` is represented here as just `phone`.\r\n- This method takes only one argument.\r\n- This method must return a `Node`.\r\n- The Class and file names of the `Property` must be, essentially, CamelCase versions of the method name. i.e. `pobox` =\u003e `POBox`, `name` =\u003e `Name`, etc. \r\n\r\nA `Property` can take input of any kind (although most take strings), but you should be aware that there isn't a obvious mechanism (apart from reading the source code) to know what specific format a `Property` might be expecting its argument to be in, so keep user experience in mind when building these things.\r\n\r\n### Scheme\r\n\r\nA `Scheme` is the top dog; the final thing Schemer exists to do. They are surprisingly simple. A `Scheme` has to do the following:\r\n\r\n- `extend` the class `AlwaysBlank\\Schemer\\Scheme\\Scheme`. This actually probably most of the functionality.\r\n- Implement a public static method called `wrap`. It takes two arguments: `content`, which is a string, and `args` which is an optional array. It returns a `Node`. Use this method to set the properties (i.e. `itemprop`) that the element that contains this `Scheme` will need.\r\n- `use` any `Property` Traits that you want this `Scheme` to have access to. This defines the arguments it will accept.\r\n\r\nHere's an example `Scheme`:\r\n\r\n```php\r\nclass Example extends AlwaysBlank\\Schemer\\Scheme\\Scheme\r\n{\r\n    use City;\r\n    use Name;\r\n    \r\n    public static function wrap(string $content, array $args = []): Node\r\n    {\r\n        return Node::add(array_merge([\r\n            'itemscope'     =\u003e true,\r\n            'itemtype'      =\u003e 'http://schema.org/Example',\r\n            'content'       =\u003e $content,\r\n            'tag'           =\u003e 'section',\r\n            'attributes'    =\u003e [\r\n                'data-section' =\u003e 'example',\r\n            ],\r\n        ], $args));\r\n    }\r\n```\r\n\r\n#### Aliases\r\n\r\nWhen defining Nodes, you can use abbreviations for the properties. This may be useful if you find yourself in a situation where you need to type up a lot of Nodes. \r\n\r\nSupported abbreviations are:\r\n\r\n| Property   \t| Alias(es)     \t|\r\n|------------\t|---------------\t|\r\n| itemscope  \t| scope, iscope \t|\r\n| itemprop   \t| prop, iprop   \t|\r\n| itemtype   \t| type, itype   \t|\r\n| attributes \t| attr, attrs   \t|\r\n\r\nThis will create a `Scheme` called `Example` that will understand `city` and `name`, and will use a `\u003csection\u003e` with an additional attribute of `data-section=\"example\"`.\r\n\r\n### Going Further\r\n\r\nIf you have more questions about how these things work, I encourage you to dig into the source code. I've made an effort to document the code clearly inline, and keep everything simple and clear.\r\n\r\n## Debugging\r\n\r\nTo debug Schemer, set the PHP constant `ALWAYSBLANK_SCHEMER_DEBUG` to `true`. As with any debug flag, I don't recommend you set this in production.\r\n\r\nCurrently the debug flag has the following effects (this list may expand with time):\r\n\r\n- Stop suppressing exceptions when calling `Property`s with bad arguments.\r\n\r\n## Limitations\r\n\r\n### Nesting\r\n\r\nThe output of a `Scheme` is generally pretty \"flat\" in the sense that it's just a list of elements that aren't nested within on another. (The exception being if the `Scheme` in question includes other `Scheme`s as `Property`s, as `LocalBusiness` does with `Address`.) This is more or less by design: So far as I can tell, building a system that would allow a user to dynamically modify the nesting of arbitrary elements would create an extreme amount of complexity in a library that is supposed to be very simple. In general, `Scheme`-level content isn't usually heavily nested anyway (apart from the aforementioned exceptions).\r\n\r\nIf you find yourself needing a more complex structure, Schemer is design to be flexible enough that you can build `Scheme`s and `Property`s \"on the fly\"—or even just use `Property`s directly without the need for a `Scheme`. \r\n\r\n### Options\r\n\r\nThis library is currently limited in terms of what it \"understands\". While it can be easily expanded, I have no intention of ever making it comprehensive with respect to the full schema.org specifications. Polite feature requests are always welcome (and good PRs will likely be merged without question), but please keep in mind that the primary purpose of this library is to make a small slice of repetitive tasks less repetitive, not to solve all microdata-related problems.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falwaysblank%2Fschemer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falwaysblank%2Fschemer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falwaysblank%2Fschemer/lists"}