Reference
- Overview
- Core Concepts
- Configuration
- Services & Adapters
- Kernel Methods
- Graph
- Nodes
- Edges
- IDs
- Notifications
- Signals
- Hooks
- Handlers & Injectables
- ACL (Access-Control Lists)
- Project Directory
- Compiling a Recipe
- More Resources...
1. Overview
Written in PHP7, Phở Kernel is a programmable interface, as well as a configuration, events broker for distributed micro social graphs.
Phở Kernel is not static, which means you can run multiple kernel instances in a thread, and you can halt and reboot each thread as many times as needed.
Here is a sample bootstrap script for pho-kernel:
include("vendor/autoload.php");
$configs = array(
"services"=>array(
"database" => "redis://127.0.0.1:6379",
"storage" => "filesystem:// /tmp"
)
);
$kernel = new Pho\Kernel\Kernel($configs);
$kernel->boot();
$network = $kernel->graph();
$founder = $kernel->founder();
array_walk($network->members(), function($key, $item) {
printf("A %s node with id: %s", get_class($item), $item->id());
});
In a nutshell;
- Firstly, we form the kernel object by passing a configuration variable that holds all our preferences and server-related settings.
- Secondly, we boot it up. Optionally you can pass the founder object as a parameter, which would initialize the kernel (for a single time) with that user as the founder. This may be useful if you have a custom Actor node.
- Finally, we start playing with it by calling
graph()
andfounder()
methods.
2. Core Concepts
Phở Kernel allows you to launch and manage social graphs. Just like any other graph, social graphs are also formed by "nodes" and their relationships identified by "edges".
In Phở's GAO model, a social network cconsists of three type of entities:
- Graphs
- Actors
- Objects
Each of these types have their own particular characteristics which we will discuss in the chapters 5, 6 and 7.
Both edges and nodes are "entities" and they're represented in the database with a cryptographically secure unique identifier in the form of: "4a406464850849e9b13ac38d0a67c157". For more information on Pho IDs, check out section 9.
The figures below illustrate a mini social network, inspired by the movie Matrix where nodes are represented by:
- Squares => Graph,
- Triangles => Actor,
- Circles => Object
and edges by arrows.
This is what the figure looks like with the Actor/Write edges:
And this is what it looks like with the Actor/Subscribe edges:
This tiny social network consists of:
- 5 characters (aka Actor): Neo, Morpheus, Trinity, Agent Smith and Spoon Boy.
- 2 graphs: Matrix (the network itself) and a group called Resistance Hovercraft, created by Morpheus.
- 4 objects:
- "Everything begins with a choice" a status update by Morpheus transmitted to the Matrix network,
- "Resistance is futile" a comment by Agent Smith in response to Morpheus' blog post,
- "We must save Morpheus" a blog post by Neo transmitted to the Resistance Hovercraft group after Agent Smith captures Morpheus,
- and "Kiss me" a private message by Trinity transmitted to Neo before she dies.
The examples above should give you an idea about how Actor, Graph and Object nodes behave.
3. Configuration
The list of all the configuration variables you can play with can be found in pho-microkernel's defaults.php file.
array(
"services" => array(
// The adapter to use, and uri the service options.
"database" => "apcu:",
"logger" => "stdout:",
"storage" => "filesystem:",
"events" => "local:",
),
"tmp_path" => sys_get_temp_dir(), // Tmp folder to store files (e.g. uploads)
"root_path" => __DIR__,
"adapter_path" => __DIR__ . DIRECTORY_SEPARATOR ." Services" . DIRECTORY_SEPARATOR . "Adapters",
"namespaces" => array(
"root" => __NAMESPACE__,
"predicates" => __NAMESPACE__."\\Predicates\\",
"services" => __NAMESPACE__."\\Services\\",
"nodes" => __NAMESPACE__."\\Nodes\\",
"edges" => __NAMESPACE__."\\Edges\\",
),
"log_level" => "WARNING", // INFO
"database_key_separator" => "/",
"default_objects" => array(
"graph" => Standards\Graph::class,
"founder" => Standards\Founder::class,
"space" => Standards\Space::class,
// "actor" => ... // not set here.
"editors" => Standards\VirtualGraph::class
)
);
4. Services & Adapters
Kernel services are defined in pho-microkernel. You can access a service by calling $kernel->$service_name()
where $service_name is the name of the service in lowercase. Currently standard services on Pho-Kernel are:
Service Type | Description | Base Adapter | Distributed Adapter | Cloud Adapter | Implements |
---|---|---|---|---|---|
Database | As a proof of truth and to store fundamental graph information. | APCU | Redis | ... | |
Events | To make the kernel more flexible with event-driven programming. | Local | ZeroMQ | ... | ... |
Index | To make search and database query easier and faster. | MySQL | ElasticSearch | ... | ... |
Logger | To log for errors or debug information. | Stdout or File | Scribe | ... | ... |
Storage | To store and retrieve plaintext or binary files such as photos, videos, zip files etc. | Filesystem | OpenStack Swift | AWS S3 | .. |
To illustrate, you can access database with; $kernel->database()
, and logger with; $kernel->logger()
.
5. Kernel Methods
5.1 boot
Once you create the kernel object, you need to boot it up with:
$kernel->boot();
Or if you have a custom founder (based on a non-standard Actor object) and you're running the kernel for the first time, you should pass it here:
$kernel->boot($founder);
If this is not the first time the kernel is booted up, the argument will be ignored.
5.2 space & graph
Once the kernel is booted up, you can query the outer and inner graphs as follows:
$space = $kernel->space(); // returns the outer graph.
$graph = $kernel->graph(); // returns the actual graph that all your nodes will become part of.
The outer graph, or "space" is a super-graph that has one and only one element, that is the graph.
5.3 gs
To query a node, you just use the kernel's gs()
method, which is similar to UNIX' filesystem. You must know the ID of the node.
To retrieve a node with the given id:
$node = $kernel->gs()->node("aa406464-8508-49e9-b13a-c38d0a67c157");
Similarly, you can retrieve an edge with its ID using $kernel->gs()->edge("edge-id")
5.3 Services
You call a service with $kernel->$service_name
where $service_name represents the service keyword in lower case. For more details, see Chapter 4.
5.4 Creating new nodes
To create a node in the graph, you just need to create the object by passing the kernel ($kernel) as its first argument. By default:
- Actor objects have a minimum of two constructor arguments; $kernel and $context (must implement \Pho\Framework\ContextInterface, for example: $kernel->space() which is the outer graph or $kernel->graph() which is the inner graph)
- Object and Graph nodes may be created with at least three constructor arguments; $kernel, $context and $creator (which is an Actor object, the creator of this object). However, please note, graphs and object nodes shall not be created manually. The recommended way to create such nodes is via formative edges, which we will see later.
6. Graph
Graph contains objects that implement NodeInterface interface, such as Node and Subgraph, but not Edge.
Method | Parameter(s) | Description | Returns |
---|---|---|---|
id | Always returns "." as ID obj. | ID | |
add [*] | NodeInterface $node | Adds a new node | void |
count | Counts the # of member nodes. | int | |
contains | ID $node_id | Checks if a node is a member | bool |
get | ID $node_id | Fetches a member | NodeInterface |
remove | ID $node_id | Removes a member | void |
members | Lists members in object form | array\<NodeInterface> | |
toArray | Lists member ref.s in ID form | array\<ID> | |
loadNodesFromArray | array $nodes | Array of NodeInteface objects | void |
loadNodesFromIDArray | array $node_ids | Array of node IDs in string | void |
7. Nodes
Nodes(aka Particles) implement the following API:
Method | Parameter(s) | Description | Returns |
---|---|---|---|
id | Retrieves its ID | ID | |
label | Returns the class name | string | |
isA | string $class_name | Validates object class | bool |
attributes | Returns the attributes class | AttributeBag | |
destroy | Readies object for destruction | void | |
toArray | Lists member ref.s in ID form | array | |
edges | Retrieves the EdgeList object that interfaces its edges. | EdgeList | |
context | Retrieves its context | GraphInterface | |
inDestruction | Reserved to use by observers to understand the state of the node. | bool |
Nodes (or Particles) have three sub-types:
7.1 Graphs (aka SubGraphs)
Graphs contain other nodes. This is their one and only function. A social network itself is a Graph. We call it the "Network". Another typical type of Graph that most social networks have is Groups.
7.2 Actors
Actors can do three things;
- Read
- Write (forming new objects or editing them)
- Subscribe (to get Notification from nodes)
In social network context, social network members are the Actors. To illustrate this, take the example of Facebook. Facebook itself is a Graph. The groups and friend lists on Facebook are also Graphs (micro graphs). And you, as a Facebook member or the pages you own are Actors, because they like (subscribe) and respond (write) objects.
7.3 Objects
Objects are what social networks are centered around. They are the fundamental units of sharing. A photo or status update can be examples of object. The only edge that originates from Objects is Edge, which does the exact opposite of Subscribe, e.g. sending Notifications.
8. Edges
An Edge (aka lines or arcs in graph theory) are used to represent the relationships between Nodes of a Graph. Therefore it is a fundamental unit graphs.
Method | Parameter(s) | Description | Returns |
---|---|---|---|
id | Retrieves its ID | ID | |
label | Returns the class name | string | |
isA | string $class_name | Validates object class | bool |
attributes | Returns the attributes class | AttributeBag | |
destroy | Readies object for destruction | void | |
toArray | Lists member ref.s in ID form | array | |
tail | Retrieves the tail node of the edge. | TailNode [*] | |
tailID | Retrieves the tail node's ID. | ID | |
head | Retrieves the head node of the edge. | HeadNode [*] | |
headID | Retrieves the head node's ID | ID | |
predicate | Retrieves the predicate | PredicateInterface | |
connect | NodeInterface $head | Connects the edge to a head node. | void |
orphan | Checks if the edge fails to possess a tail or a head | bool |
9. IDs
Pho IDs are immutable and come in the format of cryptographically secure, similarly to UUIDv4, though not the same.
Pho IDs are used to define all graph entities, e.g nodes and edges. It is 16 bytes (128 bits) long similar to UUID, but the first 8 bits are reserved to determine entity type, while the UUID variants are omitted. Hence, Pho ID provides 15 bytes and 8 bits of randomness.
The Graph ID defaults to nil (00000000000000000000000000000000), or 32 chars of 0. It may may be called with ID::root()
Even at scale of billions of nodes and edges, the chances of collision is identical to zero.
You can generate a new ID with $id_object = ID::generate($entity)
,
where $entity is any Pho entity, and fetch its string representation with
PHP type-casting; (string) $id_object
.
Entity headers will be as follows:
- 0: Graph
- 1: Unidentified Node
- 2: SubGraph Node
- 3: Framework\Graph Node
- 4: Actor Node
- 5: Object Node
- 6: Unidentified Edge
- 7: Read Edge
- 8: Write Edge
- 9: Subscribe Edge
- 10: Mention Edge
- 11: Unidentified
10. Notifications
Notifications are the messages passed between notifiers and objects, or subscribers and their subscriptions. Notifications constitute a basic component of all social-enabled apps.
For more information, take a look at
- AbstractNotification.php class.
- and ObjectOut/MentionNotification.php class.
to see how notifications works.
Notifications are called by the execute()
method of the edges. Example: ObjectOut/Mention.php and ActorOut/Write.php
11. Signals
One of the best features of the Pho Kernel is that it is event-driven. Anytime a new node or edge is created, deleted, edited, a signal is emitted. If set, a listener object (a Callable) can process the signal in real-time.
Available events are:
Graph
- node.added: when a new node is added to the graph.
- modified: when the graph is modified with a node addition or removal.
Edge
- modified: when the edge is modified either by connecting or by its attribute bag.
- deleting: when the edge is being deleted.
SubGraph
- modified: when the subgraph is modified by its attribute bag.
- edge.created: when there is a new edge originating from this subgraph.
- edge.connected: when the orphan edge of this subgraph is connected to a head.
- deleting: when the subgraph is being deleted.
Node
- modified: when the node is modified by its attribute bag.
- edge.created: when there is a new edge originating from this node.
- edge.connected: when the orphan edge of this node is connected to a head.
- deleting: when the node is being deleted.
- edge.registered: (string $direction, string $class): called by particles when registering edges. This function may be used extra edges easily and independently, without extending the constructor itself.
- notification.received: (AbstractNotification $notification): called when the actor received a notification.
Graphsystem
- graphsystem.touched: when an entity has been persisted to disk.
- graphsystem.node_deleted: when a node is removed from persistent state.
- graphsystem.edge_deleted: when an edge is removed from persistent state.
Example usage:
$node->on("modified", function() use ($node) {
$node->persist();
});
12. Hooks
Hooks allow developers to intercept certain functions that may benefit from hydration at higher-levels. Hydration takes place with persistent objects which, once deserialized, may lose some of their object associations.
To illustrate, when you persist a node object, its EdgeList object may turn into an array rather than a full-blown object, which would be hard and expensive to store. Then, in order to retrieve the edge list, you can use the IDs and tap into your database in separate calls, which would enhance the performance of your app. Lib-Graph's hooks come into play in such scenarios, because you can intercept these getter methods and inject value by leveraging the information stored in your database.
You can use hooks as follows:
$node->hook("get", function($id) use ($existing_node) {
return $existing_node;
});
where
- The first argument is the hook key, in string format.
- The second argument is a PHP closure (you can pass it as a variable too).
Below you can see a full list of entities that support hooks and their keys.
Graph and SubGraph:
- get(ID $node_id): called when
get(ID $node_id)
can't find the object in$nodes
. Enables you to access$node_ids
to fetch the object from external sources. The return value is NodeInterface. - members: called when
members()
can't find any objects in$nodes
. Enables you to access$node_ids
to fetch the objects from external sources. The return value is array (of NodeInterface objects).
Edge:
- head: called when
head()
can't find the head node. Enables you to access$head_id
to fetch it from external sources. The return value is NodeInterface. - tail: called when
tail()
can't find the tail node. Enables you to access$tail_id
to fetch it from external sources. The return value is NodeInterface. - predicate: called when
predicate()
can't find the predicate object. Enables you to access$predicate_label
to recreate it or fetch from external sources. The return value is PredicateInterface.
Node and SubGraph:
- context: called when
context()
can't find the context. Enables you to access$context_id
to fetch it from external sources. The return value is GraphInterface. - edge(string $edge_id): called to retrieve an edge object from external sources. The return value must be an EdgeInterface
- creator(): called when
creator()
can't find the creator. Enables you to access$creator_id
to fetch it from external sources. This can be used with any particle; be it an Actor, Object or Graph. The return value is Actor.
Notifications:
- edge(): called when
edge()
(in NotificationList.php) can't find the edge. Enables you to access$edge_id
to fetch it from external sources. The return value is \Pho\Lib\Graph\EdgeInterface.
13. Handlers and Injectables
Handlers
Handlers are virtual methods in use by particles. Virtual methods are created to handle setters and getters in respect to edges and fields. There are four (4) types of handlers:
- Get: to retrieve an edge. Default implementation.
- Set: to create an edge. Default implementation.
- Form: not only to create an edge, but also its head node. Default implementation.
- Has: to check if such an edge does exist. Default implementation.
These adapters can be replaced, or more can be added using handler adapters via registerHandlerAdapter(string $handler_key, string $handler_class)
function. For example:
$this->registerHandler(
"form",
\Pho\Kernel\Foundation\Handlers\Form::class);
Injectables
Objects that implement the InjectableInterface and use the InjectableTrait are easily extensible, with a plug-in variable architecture.
In order to inject a variable to an injectable object, just use
$obj->inject("key", $booster);
Then, the $obj
will be able to use the $booster
object internally via:
$this->injection("key");
The use Injectable is discouraged, as it may represent security holes if not used properly. But you can use it when you must. Currently the only class that implements it by default is AbstractEdge.
14. ACL (Access Control Lists)
Acl stands for "access-control-lists". Pho handles access to nodes and graphs similarly to how UNIX handles access to files and folders, hence the name.
With Pho:
- a node is the UNIX equivalent of a file.
- a graph is the UNIX equivalent of a directory.
In terms of privileges;
- read remains the same for both.
- write reamins the same for both.
- UNIX' execute is Pho's subscribe.
- In addition, Pho introduces the "manage" privilege.
Due to this additional privilege (e.g. "manage"), Pho uses a hexadecimal system to manage privileges, in contrast to UNIX' octal.
Plus, in terms of privilege groups, Pho introduces an additional one, called "subscribers":
- UNIX' u (users) remain the same. The u group is the owner of the object (or the actor itself, if it's an actor).
- s (subscriber) is a new privilege group. It consists of all the subscribers of this node. The subscribers include the head nodes of any edge that extends the Subscribe edge.
- g (graph) is same as UNIX' g (group). It is the group of actors that belong to the same context with the "u" of this node.
- o (others) is same as UNIX' o. It is "others", meaning actors that belong to graphs not included by the context that the "u" belongs to.
Additionally, one can set up fine-grained privileges per actor and graph, using Pho's advanced access control lists, which is again, inspired by UNIX' access control lists, but slightly different.
Again, similarly to UNIX, Pho also has "sticky-bit" which ensures the privileges of an entity (or object) can only be changed by its owner, and not by the admins.
One can change a node's privileges with this simple command:
$node->acl()->chmod(0x1e741);
where the parameter is (from left-to-right):
- 0x: is PHP's hexadecimal declaration. So it must be there.
- 1: is the sticky bit. Similar to UNIX, it may be 0 or 1.
- e: is the "u". This gives the u "manage" privilege, in addition to "read", "write" and "subscribe". So it's 1(subscribe) + 2(write) + 4(read) + 8(manage) = 15("e" in hexadecimal).
- 7: is the "s". Subscribers can write to, read and subscribe to this node, but not manage it.
- 4: is the "g". People in the same graph with this node.
- 1: is the "o" -- outsiders.
So for example, if this $node was a group (like a Facebook Group), this means:
- Only the group owner can edit or assign new privileges.
- The group managers can do anything, except, see above.
- The members (subscribers) of this group can post (write) and read anything.
- The people who are part of this network can read and subscribe the contents of this group, which means it's a read-only public one.
- The outsiders can only see the description of ../.
The permissions table is as follows;
Admin | Read | Write | Execute (Subscribe) | |
---|---|---|---|---|
Actor | Manage profile | See full profile. | Edit profile | Follow/become friends (or if friends already, react) |
Object | Manage reactions | Read | Edit | Subscribe/react |
Graph | Moderate/profile | Read contents | Post content | Subscribe |
15. Project Directory
Pho is designed in microservices architecture. This is a directory of Pho stack projects; it starts with foundational packages and goes up to the user-level.
Github | Description |
---|---|
pho-lib-graph | General purpose graph library. |
pho-framework | A stateless framework that establishes the core principles of the Pho stack. |
pho-microkernel | Augments the framework with services and ACL (access-control-lists), rendering it stateful. |
pho-kernel | A basic implementation of the pho-microkernel |
pho-cli | Command-line interface to help compile graphql files and initialize projects. |
pho-server-rest | REST APIs that can interface with any programming language or via HTTP. |
In addition, there are several repositories that help the aforementioned ones:
Github | Description |
---|---|
pho-lib-graphql-parser | General purpose GraphQL schema parser. Used by pho-compiler. |
pho-compiler | Compiles the GraphQL files into PHP interpretables. Used by pho-cli. |
Last but not least, the REST API language-bindings can be found at https://github.com/pho-clients. Pho-microkernel service adapters can be found at https://github.com/pho-adapters. Plus, a number of sample GraphQL implementations can be found under https://github.com/phonetworks/pho-recipes
16. Compiling a Recipe
Here is the steps to compile a recipe. The Web recipe (https://github.com/pho-recipes/Web) will be used which is also the recipe used for GraphJS.
-
Clone the repo:
https://github.com/pho-recipes/Web
.This contains source files that will be used to generate PHP files.
-
Clone the repo:
https://github.com/phonetworks/pho-cli
and install composer dependencies.This contains CLI to generate files from recipe using remote compiler.
- Run the following command from the directory of pho-cli:
./bin/php.php build ~/php-recipes-web ~/pho-recipes-web-compiled
The first argument is the source directory containing graphql schema. The second argument is the destination directory where the generated files are placed.
Note: The repo of each pho-recipes contains .compiled directory which consists the latest compiled code.
17. More resources...
For a full list of Phở Kernel classes and methods, refer to:
Any questions/comments? Please say below: