Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
XiaoHuizhe committed Apr 5, 2017
0 parents commit 20f5547
Show file tree
Hide file tree
Showing 13 changed files with 707 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .idea/apns2.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/copyright/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

334 changes: 334 additions & 0 deletions .idea/workspace.xml

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "dashi/apns2",
"type": "library",
"license": "MIT",
"homepage": "https://github.com/huxia/apns2",
"description": "Simple HTTP2 API for Apple Push Notification service (APNs)",
"authors": [
{
"name": "Huizhe",
"email": "huizhe.xiao@gmail.com"
}
],
"keywords": [
"apns",
"http2",
"apple",
"push",
"notification",
"simple"
],
"require": {},
"autoload": {
"psr-4": {
"Apns2\\": "src/"
}
},
"config": {
"bin-dir": "bin"
}
}
53 changes: 53 additions & 0 deletions src/BaseDataObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
namespace Apns2;

use JsonSerializable;

/**
* base class for dealing with json data
* @package Apns2
*/
abstract class BaseDataObject implements JsonSerializable
{

public abstract function __construct($array = []);

/**
* @param array|object $json data to load from
* @param array $childClasses definition of child objects, schema [ $varName => $className ]
*/
public function loadFromJSON($json, $childClasses = [])
{
if ($json) {
foreach ($json as $k => $v) {
$varName = Utils::camelcaseToHyphenJoined($k);
if (!empty($childClasses[$varName]) && (is_object($v) || is_array($v))) {
$subObjectClassName = $childClasses[$varName];
// sub-object
$this->$varName = new $subObjectClassName($v);
} else {
$this->$varName = $v;
}
}
}
}

/**
* default Serialize implementation
* @return array
*/
public function jsonSerialize()
{
$result = [];
foreach ($this as $k => $v) {
if ($v instanceof JsonSerializable) {
$result[Utils::camelcaseToHyphenJoined($k)] = $v->jsonSerialize();
} else {
$result[Utils::camelcaseToHyphenJoined($k)] = $v;
}
}
return array_filter($result, function (&$v) {
return $v !== null;
});
}
}
99 changes: 99 additions & 0 deletions src/Connection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
namespace Apns2;

/**
* An http2 curl connection
* @package Apns2
*/
class Connection extends BaseDataObject
{
public $sandbox;
public $certPath;
private $ch;


/**
* Connection constructor.
* @param array $data example: ['sandbox' => true, 'cert-path' => '/var/www/config/http2.pem']
*/
public function __construct($data = [])
{
$this->loadFromJSON($data);
}

protected function sendOne($token, $message, $options)
{


if (!defined('CURL_HTTP_VERSION_2_0')) {
define('CURL_HTTP_VERSION_2_0', 3);
}
if (!$this->ch) {
$this->ch = curl_init();
}
curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);

$beginTime = microtime(true);

$host = $this->sandbox ? 'https://api.development.push.apple.com' : 'https://api.push.apple.com';

$cert = realpath($this->certPath);
if (!$cert) {
throw new \Exception("cert path invalid: {$this->certPath}");
}
curl_setopt_array($this->ch, array(
CURLOPT_URL => "{$host}/3/device/{$token}",
CURLOPT_PORT => 443,
CURLOPT_HTTPHEADER => $options->getHeadersForHttp2API(),
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($message),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $options->curlTimeout,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSLCERT => $cert,
CURLOPT_HEADER => 1
));

$result = curl_exec($this->ch);
if ($result === false) {
throw new \Exception('Curl failed with error: ' . curl_error($this->ch));
}
$response = new Response();
$response->status = $result;
$response->code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
$response->duration = microtime(true) - $beginTime;
$response->device_id = $token;
return $response;
}

/**
* send notification to multiple tokens
* @param array $tokens
* @param array|Message $message example: ['aps' => ['alert' => 'hello', 'sound' => 'default', 'content-available' => 1]]
* @param array|Options $options example: ['apns-topic' => 'your.company', 'user-agent' => 'YourAgent/1.0', 'curl-timeout' => 10]
* @return Response[] an array of response
* @throws \Exception
*/
public function send($tokens, $message, $options)
{
$message = $message instanceof Message ? $message : new Message($message);
$options = $options instanceof Options ? $options : new Options($message);

$result = [];
foreach ($tokens as $token) {
$result [] = $this->sendOne($token, $message, $options);
}
return $result;
}

/**
* close the connection
*/
public function close()
{
if ($this->ch !== null) {
curl_close($this->ch);
$this->ch = null;
}
}
}
17 changes: 17 additions & 0 deletions src/Message.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
namespace Apns2;

class Message extends BaseDataObject
{
public $aps;

public $customData;

public function __construct($data = [])
{
$this->loadFromJSON($data, [
'aps' => MessageAPSBody::class
]);
}

}
31 changes: 31 additions & 0 deletions src/MessageAPSBody.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
namespace Apns2;

class MessageAPSBody extends BaseDataObject
{
/**
* @var string|MessageAlertBody
*/
public $alert;
/**
* @var string|null
*/
public $sound;
/**
* @var int|null
*/
public $badge;
/**
* @var int|null
*/
public $contentAvailable;


public function __construct($data = [])
{
$this->loadFromJSON($data, [
'alert' => MessageAlertBody::class
]);
}
}

38 changes: 38 additions & 0 deletions src/MessageAlertBody.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
namespace Apns2;

class MessageAlertBody extends BaseDataObject
{
/**
* @var string
*/
public $title;
/**
* @var string|null
*/
public $body;
/**
* @var string|null
*/
public $titleLocKey;
/**
* @var string|null
*/
public $titleLocArgs;
/**
* @var string|null
*/
public $actionLocKey;
/**
* @var string|null
*/
public $locKey;
/**
* @var string|null
*/
public $locArgs;
/**
* @var string|null
*/
public $launchImage;
}
50 changes: 50 additions & 0 deletions src/Options.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
namespace Apns2;

class Options extends BaseDataObject
{
/**
* @var string|null
*/
public $apnsId;
/**
* @var int|null
*/
public $apnsExpiration;
/**
* @var int|null
*/
public $apnsPriority;
/**
* @var string
*/
public $apnsTopic;

/**
* @var int
*/
public $curlTimeout = 30;

public $userAgent = 'apns2(php)';

public function __construct($data = [])
{
$this->loadFromJSON($data);
}

public function getHeadersForHttp2API()
{
$headers = array_filter($this->jsonSerialize(), function ($k) {
return preg_match('/(^user-agent$|^apns-)/i', $k);
}, ARRAY_FILTER_USE_KEY);
$result = [];
foreach ($headers as $k => $v) {
if (is_scalar($v)) {
$result[] = "$k: $v";
}
}
return $result;
}


}
14 changes: 14 additions & 0 deletions src/Response.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
namespace Apns2;

/**
* Response for sending message to specific device via APNs
* @see https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW17
* @package Apns2
*/
class Response{
public $device_id;
public $code;
public $status;
public $duration;
}
22 changes: 22 additions & 0 deletions src/Utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
namespace Apns2;

class Utils{

public static function hyphenJoinedToCamelcase($string, $capitalizeFirstCharacter = true, $joinCharRegex = '/[\-\_]/')
{
$str = str_replace(' ', '', ucwords(preg_replace($joinCharRegex, ' ', $string)));

if (!$capitalizeFirstCharacter) {
$str[0] = strtolower($str[0]);
}

return $str;
}

public static function camelcaseToHyphenJoined($str, $joinChar = '-')
{
return ltrim(strtolower(preg_replace('/[A-Z]/', '_$0', $str)), $joinChar);
}

}

0 comments on commit 20f5547

Please sign in to comment.