') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - gdbots/pbjc-php: Compiler for converting pbj schemas into jsonschema, php, js, etc. · GitHub
Skip to content

Repository files navigation

pbjc-php

Build StatusCode Climate

Compiler for converting pbj schemas into jsonschema, php, js, etc.

Language Guide

This guide describes how to use the XML language to structure your schema file syntax and how to generate data classes files.

References

Let's start by defining each of the elements and key options used across the compiler.

  • Schema: The purpose of a Schema is to define a pbj message, with the fields and related mixins (other schemas used to extend the schema capability).

  • Enum: An Enum is a collection of key-value, used in schema fields (see Enumerations below).

  • SchemaId: A Schema fully qualified name (id).

    • Schema Id: pbj:vendor:package:category:message:version
    • Schema Curie Major: vendor:package:category:message:v#
    • Schema Curie: vendor:package:category:message
    • Schema QName: vendor:message
  • SchemaVersion: Similar to semantic versioning but with dashes and no "alpha, beta, etc." qualifiers.

    • Schema Version Format: major-minor-patch

Defining A Schema

First let's look at a very simple example. Let's say you want to define a mixin schema, with slug and name fields. Here's the .xml file you use to define the schema.

<?xml version="1.0" encoding="UTF-8" ?>
<pbj-schemaxmlns="http://gdbots.io/pbj/xsd"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://gdbots.io/pbj/xsd http://gdbots.io/pbj/xsd/schema.xsd">
<schemaid="pbj:acme:blog:entity:article:1-0-0"mixin="true">
<fields>
<fieldname="slug"type="string"pattern="/^[A-Za-z0-9_\-]+$/"required="true" />
<fieldname="title"type="text"required="true" />
</fields>
</schema>
</pbj-schema>

Each schema required a few basic elements: id and fields. The id is a unique identifier follow a basic schema-id format pbj:vendor:package:category:message:version (version = major-minor-patch). The fields is an array of associated fields used by the schema. In the above example, the store schema contains a slug and a title.

Since we are creating a mixin schema, we set in the second line mixin = true.

In addition, we allow to add language specific options which will be used while generating the language output file.

Schema Field Types

The following list contains all available field types:

- big-int
- binary
- blob
- boolean
- date
- date-time
- decimal
- dynamic-field
- float
- geo-point
- identifier
- float
- int
- medium-blob
- medium-int
- medium-text
- microtime
- signed-big-int
- signed-int
- signed-medium-int
- signed-small-int
- signed-tiny-int
- small-int
- string
- text
- time-uuid
- timestamp
- tiny-int
- uuid

Default Values

When a schema is parsed, if the encoded schema does not contain a particular singular element, the corresponding field in the parsed object is set to the default value for that field. These defaults are type-specific:

- For strings, the default value is the empty string.
- For bytes, the default value is empty bytes.
- For bools, the default value is false.
- For numeric types, the default value is zero.
- For each of the other field types, the default value is null.

Enumerations

When you're defining a schema, you might want one of its fields to only have one of a pre-defined list of values. For example, let's say you want to add a Reason enum field, where the values can be INVALID, FAILED or DELETED.

<fields>
<fieldname="failure_reason"type="string-enum">
<default>invalid</default>
<enumid="acme:blog:publish-status" />
</field>
</fields>

The define the enum in enums.xml:

<enumsnamespace="acme:blog">
<enumname="publish-status"type="string">
<optionkey="PUBLISHED"value="published" />
<optionkey="DRAFT"value="draft" />
<optionkey="PENDING"value="pending" />
<optionkey="EXPIRED"value="expired" />
<optionkey="DELETED"value="deleted" />
</enum>
<enums>

From the above example you can see we defined the enum keys and values for a specific schema and called it directly from the field.

Note: We can also define the PHP namespace where the enum class will be generated to.

There are 2 kinds of enum types, StringEnum and IntEnum. We separated to simplified the field type and values.

Note: major database for example MySQL, DynamoDB and other define enum based on type - string or int.

Using Message Types

You can use Message and MessageRef as field types. For example, let's say you wanted to include related messages in each Story schema:

<fieldname="failed_request"type="message">
<any-of>
<curie>gdbots:pbjx:mixin:request</curie>
</any-of>
</field>

The any-of attribute define the message id that will be used to pull the message details.

Full Schema Options

<?xml version="1.0" encoding="UTF-8" ?>
<pbj-schemaxmlns="http://gdbots.io/pbj/xsd"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://gdbots.io/pbj/xsd http://gdbots.io/pbj/xsd/schema.xsd">
<schemaid="{pbj:vendor:package:category:message:major-minor-patch}"mixin="{bool}"extends="{pbj:vendor:package:category:vmajor}"
>
<fields>
<fieldname="{/^([a-zA-Z_]{1}[a-zA-Z0-9_]+)$/}"type="{\Gdbots\Pbjc\Type\Type}"required="{bool}"min="{int}"max="{int}"precision="{int}"scale="{int}"rule="{\Gdbots\Pbjc\Enum\FieldRule}"pattern="{string}"format="{Gdbots\Pbjc\Enum\Format}"use-type-default="{bool}"overridable="{bool}"
>
<default>{string}</default>
<enumid="{vendor:package:enum}" />
<any-of>
<curie>{pbj:vendor:package:category}</curie>
<!-- ... -->
</any-of>
<php-options>
<imports>{string}</imports>
<class-name>{string}</class-name>
<default>{string}</default>
</php-options>
<js-options>
<imports>{string}</imports>
<class-proto>{string}</class-proto>
<default>{string}</default>
</php-options>
</field>
</fields>
<mixins>
<curie-major>{pbj:vendor:package:category:vmajor}</curie-major>
<!-- ... -->
</mixins>
</schema>
</pbj-schema>
<?xml version="1.0" encoding="UTF-8" ?>
<pbj-enumsxmlns="http://gdbots.io/pbj/xsd"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://gdbots.io/pbj/xsd http://gdbots.io/pbj/xsd/enums.xsd">
<enumsnamespace="{vendor:package}">
<enumname="{string}"type="int|string">
<optionkey="{string}"value="{string}" />
<!-- ... -->
</enum>
<enums>
</enums-mapping>

Note: For each php-options you can also add dynamic tags. For example:

<php-options>
<insertion-points>
<imports>
<![CDATA[use Gdbots\Pbj\MessageRef;]]>
</imports>
<methods>
<![CDATA[/** * @param string $tag * @return MessageRef */public function generateMessageRef($tag = null){ return new MessageRef(static::schema()->getCurie(), $this->get('command_id'), $tag);}]]>
</methods>
</insertion-points>
</php-options>

Basic Usage

pbjc --language[=LANGUAGE] --config[=CONFIG]
OptionNotes
-l or --language[=LANGUAGE]The generated language [default: "php"]
-c or --config[=CONFIG]The pbjc config yaml file

Define compile settings in pbjc.yml file:

namespaces:
- <vendor1>:<package1>
- <vendor2>:<package2>languages:
php:
output: <div>manifest: <dir>/<filename>

Note: by default the compiler searches for pbjc.yml in the root folder.

About

Compiler for converting pbj schemas into jsonschema, php, js, etc.

Topics

Resources

Stars

0 stars

Watchers

7 watching

Forks

Releases

Packages

Used by

Contributors

Languages