Skip to content

Commit 9c24a86

Browse files
committed
PHP 5.3, 5.4 and 5.5 examples for "new" features
0 parents  commit 9c24a86

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+12470
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
\.idea
2+
\.hg
3+
\.hgignore
4+
\.vagrant

Coro.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
class CoHost
4+
{
5+
protected $id;
6+
7+
public function getId()
8+
{
9+
if (is_null($this->id)) {
10+
$set = $this->setId();
11+
$set->send(yield identifier());
12+
}
13+
14+
yield $this->id;
15+
}
16+
17+
public function setId()
18+
{
19+
if (is_null($this->id)) {
20+
$this->id = yield;
21+
}
22+
}
23+
}
24+
25+
function identifier()
26+
{
27+
static $i = 0;
28+
return $i++;
29+
}
30+
31+
32+
$host = new CoHost();
33+
34+
35+
$host->setId()->send(54);
36+
37+
$get = $host->getId();
38+
39+
foreach ($get as $val) {
40+
echo $val;
41+
}

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
PHP 5.3, 5.4 and 5.5 Examples
2+
=============================
3+
4+
This repository contains various examples of some of the "new" features in PHP 5.3,
5+
PHP 5.4 and PHP 5.5. It also contains some experiments that I ended up abandoning
6+
for one reason or another. This is by no means production ready code. It was built
7+
to show quick and easy to digest examples for a PHP meetup group. If you put it in your
8+
web root or start the PHP built-in web server, you should see a menu with the various
9+
code examples and running examples.
10+
11+
php -S 127.0.0.1:8000
12+
13+
Then browse to http://localhost:8000
14+
15+
If there's another feature you'd like to see examples for, please open an issue
16+
or send a pull request.

Scheduler.php

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
class Scheduler
3+
{
4+
protected $maxTaskId = 0;
5+
protected $taskMap = [];
6+
protected $taskQueue;
7+
protected $waitingForRead = [];
8+
protected $waitingForWrite = [];
9+
10+
public function __construct()
11+
{
12+
$this->taskQueue = new SplQueue();
13+
}
14+
15+
public function newTask(Generator $coroutine)
16+
{
17+
$tid = ++$this->maxTaskId;
18+
$task = new Task($tid, $coroutine);
19+
$this->taskMap[$tid] = $task;
20+
$this->schedule($task);
21+
return $tid;
22+
}
23+
24+
public function schedule(Task $task)
25+
{
26+
$this->taskQueue->enqueue($task);
27+
}
28+
29+
public function run()
30+
{
31+
$this->newTask($this->ioPollTask());
32+
33+
while(!$this->taskQueue->isEmpty()) {
34+
/** @var Task $task */
35+
$task = $this->taskQueue->dequeue();
36+
$retval = $task->run();
37+
38+
if ($retval instanceof SystemCall) {
39+
$retval($task, $this);
40+
continue;
41+
}
42+
43+
if ($task->isFinished()) {
44+
unset($this->taskMap[$task->getTaskId()]);
45+
} else {
46+
$this->schedule($task);
47+
}
48+
}
49+
}
50+
51+
public function killTask($tid)
52+
{
53+
if (!isset($this->taskMap[$tid])) {
54+
return false;
55+
}
56+
57+
unset($this->taskMap[$tid]);
58+
59+
foreach ($this->taskQueue as $i => $task) {
60+
/** @var Task $task */
61+
if ($task->getTaskId() === $tid) {
62+
unset($this->taskQueue[$i]);
63+
break;
64+
}
65+
}
66+
67+
echo "Scheduler killed task $tid<br>";
68+
return true;
69+
}
70+
71+
public function waitForRead($socket, Task $task)
72+
{
73+
if (isset($this->waitingForRead[(int) $socket])) {
74+
$this->waitingForRead[(int) $socket][1][] = $task;
75+
} else {
76+
$this->waitingForRead[(int) $socket] = [$socket, [$task]];
77+
}
78+
}
79+
80+
public function waitForWrite($socket, Task $task)
81+
{
82+
if (isset($this->waitingForWrite[(int) $socket])) {
83+
$this->waitingForWrite[(int) $socket][1][] = $task;
84+
} else {
85+
$this->waitingForWrite[(int) $socket] = [$socket, [$task]];
86+
}
87+
}
88+
89+
protected function ioPoll($timeout)
90+
{
91+
$rSocks = [];
92+
foreach ($this->waitingForRead as list($socket)) {
93+
$rSocks[] = $socket;
94+
}
95+
96+
$wSocks = [];
97+
foreach ($this->waitingForWrite as list($socket)) {
98+
$wSocks[] = $socket;
99+
}
100+
101+
$eSocks = []; // dummy
102+
103+
if (!stream_select($rSocks, $wSocks, $eSocks, $timeout)) {
104+
return;
105+
}
106+
107+
foreach ($rSocks as $socket) {
108+
list(, $tasks) = $this->waitingForRead[(int) $socket];
109+
unset($this->waitingForRead[(int) $socket]);
110+
111+
foreach ($tasks as $task) {
112+
$this->schedule($task);
113+
}
114+
}
115+
116+
foreach ($wSocks as $socket) {
117+
list(, $tasks) = $this->waitingForWrite[(int) $socket];
118+
unset($this->waitingForWrite[(int) $socket]);
119+
120+
foreach ($tasks as $task) {
121+
$this->schedule($task);
122+
}
123+
}
124+
}
125+
126+
protected function ioPollTask()
127+
{
128+
while (true) {
129+
if ($this->taskQueue->isEmpty()) {
130+
$this->ioPoll(null);
131+
} else {
132+
$this->ioPoll(0);
133+
}
134+
135+
yield;
136+
}
137+
}
138+
}

SystemCall.php

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
class SystemCall
4+
{
5+
protected $callback;
6+
7+
public function __construct(callable $callback)
8+
{
9+
$this->callback = $callback;
10+
}
11+
12+
public function __invoke(Task $task, Scheduler $scheduler)
13+
{
14+
$callback = $this->callback;
15+
return $callback($task, $scheduler);
16+
}
17+
}

Task.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
class Task
3+
{
4+
protected $taskId;
5+
protected $coroutine;
6+
protected $sendValue = null;
7+
protected $beforeFirstYield = true;
8+
9+
public function __construct($taskId, Generator $coroutine)
10+
{
11+
$this->taskId = $taskId;
12+
$this->coroutine = $coroutine;
13+
}
14+
15+
public function getTaskId()
16+
{
17+
return $this->taskId;
18+
}
19+
20+
public function setSendValue($sendValue)
21+
{
22+
$this->sendValue = $sendValue;
23+
}
24+
25+
public function run()
26+
{
27+
if ($this->beforeFirstYield) {
28+
$this->beforeFirstYield = false;
29+
return $this->coroutine->current();
30+
} else {
31+
$retval = $this->coroutine->send($this->sendValue);
32+
$this->sendValue = null;
33+
return $retval;
34+
}
35+
}
36+
37+
public function isFinished()
38+
{
39+
return !$this->coroutine->valid();
40+
}
41+
}

Vagrantfile

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# -*- mode: ruby -*-
2+
# vi: set ft=ruby :
3+
4+
Vagrant.configure("2") do |config|
5+
# All Vagrant configuration is done here. The most common configuration
6+
# options are documented and commented below. For a complete reference,
7+
# please see the online documentation at vagrantup.com.
8+
9+
# Every Vagrant virtual environment requires a box to build off of.
10+
config.vm.box = "centos-62-64-puppet"
11+
12+
# The url from where the 'config.vm.box' box will be fetched if it
13+
# doesn't already exist on the user's system.
14+
# config.vm.box_url = "http://domain.com/path/to/above.box"
15+
16+
# Create a forwarded port mapping which allows access to a specific port
17+
# within the machine from a port on the host machine. In the example below,
18+
# accessing "localhost:8080" will access port 80 on the guest machine.
19+
# config.vm.network :forwarded_port, guest: 80, host: 8080
20+
# config.vm.network :forwarded_port, guest: 80, host: 8080
21+
22+
# Create a private network, which allows host-only access to the machine
23+
# using a specific IP.
24+
config.vm.network :private_network, ip: "10.0.1.101"
25+
26+
# Create a public network, which generally matched to bridged network.
27+
# Bridged networks make the machine appear as another physical device on
28+
# your network.
29+
# config.vm.network :public_network
30+
31+
# Share an additional folder to the guest VM. The first argument is
32+
# the path on the host to the actual folder. The second argument is
33+
# the path on the guest to mount the folder. And the optional third
34+
# argument is a set of non-required options.
35+
# config.vm.synced_folder "../data", "/vagrant_data"
36+
37+
# Provider-specific configuration so you can fine-tune various
38+
# backing providers for Vagrant. These expose provider-specific options.
39+
# Example for VirtualBox:
40+
#
41+
# config.vm.provider :virtualbox do |vb|
42+
# # Don't boot with headless mode
43+
# vb.gui = true
44+
#
45+
# # Use VBoxManage to customize the VM. For example to change memory:
46+
# vb.customize ["modifyvm", :id, "--memory", "1024"]
47+
# end
48+
#
49+
# View the documentation for the provider you're using for more
50+
# information on available options.
51+
52+
# Enable provisioning with Puppet stand alone. Puppet manifests
53+
# are contained in a directory path relative to this Vagrantfile.
54+
# You will need to create the manifests directory and a manifest in
55+
# the file base.pp in the manifests_path directory.
56+
#
57+
# An example Puppet manifest to provision the message of the day:
58+
#
59+
# # group { "puppet":
60+
# # ensure => "present",
61+
# # }
62+
# #
63+
# # File { owner => 0, group => 0, mode => 0644 }
64+
# #
65+
# # file { '/etc/motd':
66+
# # content => "Welcome to your Vagrant-built virtual machine!
67+
# # Managed by Puppet.\n"
68+
# # }
69+
#
70+
# config.vm.provision :puppet do |puppet|
71+
# puppet.manifests_path = "manifests"
72+
# puppet.manifest_file = "init.pp"
73+
# end
74+
75+
# Enable provisioning with chef solo, specifying a cookbooks path, roles
76+
# path, and data_bags path (all relative to this Vagrantfile), and adding
77+
# some recipes and/or roles.
78+
#
79+
# config.vm.provision :chef_solo do |chef|
80+
# chef.cookbooks_path = "../my-recipes/cookbooks"
81+
# chef.roles_path = "../my-recipes/roles"
82+
# chef.data_bags_path = "../my-recipes/data_bags"
83+
# chef.add_recipe "mysql"
84+
# chef.add_role "web"
85+
#
86+
# # You may also specify custom JSON attributes:
87+
# chef.json = { :mysql_password => "foo" }
88+
# end
89+
90+
# Enable provisioning with chef server, specifying the chef server URL,
91+
# and the path to the validation key (relative to this Vagrantfile).
92+
#
93+
# The Opscode Platform uses HTTPS. Substitute your organization for
94+
# ORGNAME in the URL and validation key.
95+
#
96+
# If you have your own Chef Server, use the appropriate URL, which may be
97+
# HTTP instead of HTTPS depending on your configuration. Also change the
98+
# validation key to validation.pem.
99+
#
100+
# config.vm.provision :chef_client do |chef|
101+
# chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
102+
# chef.validation_key_path = "ORGNAME-validator.pem"
103+
# end
104+
#
105+
# If you're using the Opscode platform, your validator client is
106+
# ORGNAME-validator, replacing ORGNAME with your organization name.
107+
#
108+
# If you have your own Chef Server, the default validation client name is
109+
# chef-validator, unless you changed the configuration.
110+
#
111+
# chef.validation_client_name = "ORGNAME-validator"
112+
end

0 commit comments

Comments
 (0)