Compare commits
2 Commits
master
..
db5f81c294
| Author | SHA1 | Date | |
|---|---|---|---|
| db5f81c294 | |||
| 5f6f382ace |
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
FILES=$(git diff --cached --name-only --diff-filter=ACMR -- '*.php' '*.html' '*.yaml' | sed 's| |\\ |g')
|
||||
[ -z "$FILES" ] && exit 0
|
||||
|
||||
# Prettify all selected files
|
||||
echo "$FILES" | xargs ./node_modules/.bin/prettier --ignore-unknown --write
|
||||
|
||||
# Add back the modified/prettified files to staging
|
||||
echo "$FILES" | xargs git add
|
||||
|
||||
exit 0
|
||||
|
||||
-15
@@ -4,8 +4,6 @@ use System\Classes\PluginBase;
|
||||
|
||||
use App;
|
||||
use Config;
|
||||
use Illuminate\Database\DatabaseManager;
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Illuminate\Foundation\AliasLoader;
|
||||
use GermanAirlinesVa\Graphql\Classes\GraphqlServiceProvider;
|
||||
use Nuwave\Lighthouse\Subscriptions\SubscriptionServiceProvider;
|
||||
@@ -35,22 +33,9 @@ class Plugin extends PluginBase
|
||||
public function boot()
|
||||
{
|
||||
App::make('October\Rain\Support\ClassLoader')->addDirectories('graphql');
|
||||
|
||||
$this->bootPackages();
|
||||
|
||||
App::register(GraphqlServiceProvider::class);
|
||||
App::register(SubscriptionServiceProvider::class);
|
||||
|
||||
$this->app->singleton(DatabaseManager::class, function ($app) {
|
||||
return $app->make('db');
|
||||
});
|
||||
Broadcast::routes([
|
||||
'prefix' => '',
|
||||
'middleware' => 'GermanAirlinesVA\\Graphql\\Classes\\Authentication',
|
||||
]);
|
||||
|
||||
Config::set('database.connections.germanairlinesva_graphql', Config::get('germanairlinesva.graphql::connection'));
|
||||
Config::push('system.unencrypt_cookies', 'graphql-session-id');
|
||||
}
|
||||
|
||||
public function bootPackages()
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Relations
|
||||
|
||||
## GraphQLKeys
|
||||
|
||||
- BelongsTo Member (External DB, creation in Social Plugin)
|
||||
@@ -32920,7 +32920,7 @@ var _validUrl = __webpack_require__(429);
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
||||
|
||||
var options = { method: 'post', headers: { 'Content-Type': 'application/json' } };
|
||||
var endpoint = 'http://' + window.location.host + '/graphql/'; // Initial
|
||||
var endpoint = 'http://localhost/graphql/'; // Initial
|
||||
|
||||
var defaultQuery = '\n# Welcome to GraphiQL\n#\n# GraphiQL is an in-browser tool for writing, validating, and\n# testing GraphQL queries.\n#\n# Type queries into this side of the screen, and you will see intelligent\n# typeaheads aware of the current GraphQL type schema and live syntax and\n# validation errors highlighted within the text.\n#\n# GraphQL queries typically start with a "{" character. Lines that starts\n# with a # are ignored.\n#\n# An example GraphQL query might look like:\n#\n# {\n# field(arg: "value") {\n# subField\n# }\n# }\n#\n# Keyboard shortcuts:\n#\n# Run Query: Ctrl-Enter (or press the play button above)\n#\n# Auto Complete: Ctrl-Space (or just start typing)\n#\n# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n# Default endpoint is an instance of https://www.graph.cool/\n# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\nquery {\n countries {\n name\n }\n}\n';
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php namespace GermanAirlinesVa\Graphql\Classes;
|
||||
|
||||
use Closure;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
class Authentication
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($request->header('Gql-Session') === 'SUPER_SECRET_KEY_HEADER') {
|
||||
return '';
|
||||
} else {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php namespace GermanAirlinesVa\Graphql\Classes;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Http\Request;
|
||||
use GraphQL\Type\Definition\ResolveInfo;
|
||||
use Nuwave\Lighthouse\Subscriptions\Subscriber;
|
||||
use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription;
|
||||
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
|
||||
|
||||
class Dummy extends GraphQLSubscription
|
||||
{
|
||||
/**
|
||||
* Check if subscriber is allowed to listen to the subscription.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(Subscriber $subscriber, Request $request): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter which subscribers should receive the subscription.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber
|
||||
* @param mixed $root
|
||||
* @return bool
|
||||
*/
|
||||
public function filter(Subscriber $subscriber, $root): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode topic name.
|
||||
*
|
||||
* @param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber
|
||||
* @param string $fieldName
|
||||
* @return string
|
||||
*/
|
||||
public function encodeTopic(Subscriber $subscriber, string $fieldName): string
|
||||
{
|
||||
// Create a unique topic name based on the `author` argument
|
||||
return Str::snake($fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode topic name.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param \App\Post $root
|
||||
* @return string
|
||||
*/
|
||||
public function decodeTopic(string $fieldName, $root): string
|
||||
{
|
||||
return Str::snake($fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the subscription.
|
||||
*
|
||||
* @param \App\Post $root
|
||||
* @param array<string, mixed> $args
|
||||
* @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context
|
||||
* @param \GraphQL\Type\Definition\ResolveInfo $resolveInfo
|
||||
* @return mixed
|
||||
*/
|
||||
public function resolve($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): array
|
||||
{
|
||||
return [$root];
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ class SchemaSourceProvider implements LighthouseSchemaSourceProvider
|
||||
public function getSchemaString(): string
|
||||
{
|
||||
// root types
|
||||
$schema = "
|
||||
$schema = '
|
||||
type Query {
|
||||
dummy: Boolean
|
||||
}
|
||||
@@ -76,11 +76,7 @@ class SchemaSourceProvider implements LighthouseSchemaSourceProvider
|
||||
type Mutation {
|
||||
dummy: Boolean
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
dummy: Boolean @subscription(class: \"GermanAirlinesVa\\\\Graphql\\\\Classes\\\\Dummy\")
|
||||
}
|
||||
";
|
||||
';
|
||||
// schema
|
||||
$schema .= collect($this->getGraphMap())->implode('schema', '');
|
||||
return $schema;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"type": "october-plugin",
|
||||
"description": "None",
|
||||
"require": {
|
||||
"nuwave/lighthouse": "^5.0",
|
||||
"composer/installers": "~1.0"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-22
@@ -4,27 +4,6 @@ use Cms\Classes\Theme;
|
||||
use GermanAirlinesVa\Graphql\Models\Settings;
|
||||
|
||||
return [
|
||||
'connection' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DATABASE_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => 'germanairlinesva_graphql',
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => 'InnoDB',
|
||||
'options' => extension_loaded('pdo_mysql')
|
||||
? array_filter([
|
||||
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
|
||||
])
|
||||
: [],
|
||||
],
|
||||
'packages' => [
|
||||
'nuwave/lighthouse' => [
|
||||
'config_namespace' => 'lighthouse',
|
||||
@@ -168,7 +147,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
//'debug' => \GraphQL\Error\Debug::INCLUDE_DEBUG_MESSAGE | \GraphQL\Error\Debug::INCLUDE_TRACE,
|
||||
'debug' => \GraphQL\Error\Debug::INCLUDE_DEBUG_MESSAGE | \GraphQL\Error\Debug::INCLUDE_TRACE,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php namespace GermanAirlinesVa\Graphql\Controllers;
|
||||
|
||||
use Backend\Classes\Controller;
|
||||
|
||||
class GraphqlKey extends Controller
|
||||
{
|
||||
public $implement = ['Backend\Behaviors\ListController'];
|
||||
|
||||
public $listConfig = 'config_list.yaml';
|
||||
|
||||
public $requiredPermissions = ['germanairlinesva.graphql.master'];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
<?php namespace GermanAirlinesVa\Graphql\Controllers;
|
||||
|
||||
use Backend\Classes\Controller;
|
||||
use BackendMenu;
|
||||
|
||||
class Playground extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public $requiredPermissions = ['germanairlinesva.graphql.master'];
|
||||
BackendMenu::setContext('GermanAirlinesVa.Graphql', 'menu', 'playground');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<div data-control="toolbar">
|
||||
<button
|
||||
class="btn btn-default oc-icon-trash-o"
|
||||
disabled="disabled"
|
||||
onclick="$(this).data('request-data', {
|
||||
checked: $('.control-list').listWidget('getChecked')
|
||||
})"
|
||||
data-request="onDelete"
|
||||
data-request-confirm="<?= e(trans('backend::lang.list.delete_selected_confirm')) ?>"
|
||||
data-trigger-action="enable"
|
||||
data-trigger=".control-list input[type=checkbox]"
|
||||
data-trigger-condition="checked"
|
||||
data-request-success="$(this).prop('disabled', true)"
|
||||
data-stripe-load-indicator
|
||||
>
|
||||
<?= e(trans('backend::lang.list.delete_selected')) ?>
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,11 +0,0 @@
|
||||
list: $/germanairlinesva/graphql/models/graphqlkey/columns.yaml
|
||||
modelClass: GermanAirlinesVa\Graphql\Models\GraphqlKey
|
||||
title: GraphQL Keys
|
||||
noRecordsMessage: 'backend::lang.list.no_records'
|
||||
showSetup: true
|
||||
showCheckboxes: true
|
||||
recordsPerPage: 20
|
||||
toolbar:
|
||||
buttons: list_toolbar
|
||||
search:
|
||||
prompt: 'backend::lang.list.search_prompt'
|
||||
@@ -1 +0,0 @@
|
||||
<?= $this->listRender() ?>
|
||||
+3
-11
@@ -1,3 +1,4 @@
|
||||
title = "CMS"
|
||||
==
|
||||
<?php
|
||||
use Cms\Classes\Theme;
|
||||
@@ -13,17 +14,8 @@ function resolveCmsContent($root, $args) {
|
||||
?>
|
||||
==
|
||||
# Content
|
||||
extend type Query {
|
||||
cmsContent(name: String!): CmsContent!
|
||||
}
|
||||
|
||||
type CmsContent {
|
||||
fileName: String!
|
||||
content: String
|
||||
markup: String
|
||||
parsedMarkup: String
|
||||
}
|
||||
|
||||
extend type Query { cmsContent(name: String!): CmsContent! }
|
||||
type CmsContent { fileName: String! content: String markup: String parsedMarkup: String }
|
||||
# CMS-wide types
|
||||
# Backend\Models\User
|
||||
type BackendUser { id: ID! }
|
||||
|
||||
+6
-18
@@ -1,3 +1,4 @@
|
||||
title = "Fleet"
|
||||
==
|
||||
==
|
||||
# Content
|
||||
@@ -5,23 +6,10 @@ extend type Query {
|
||||
aircraft: [Aircraft] @all(model: "GermanAirlinesVa\\Fleet\\Models\\Aircraft")
|
||||
aircraftType: [AircraftType] @all(model: "GermanAirlinesVa\\Fleet\\Models\\AircraftType")
|
||||
}
|
||||
extend type Subscription {
|
||||
aircraftAdded: Aircraft @subscription(class: "GermanAirlinesVa\\Fleet\\Classes\\AircraftAdded")
|
||||
}
|
||||
extend type Mutation {
|
||||
addAircraft(aircraft_type_id: ID!, home_airport_id: ID!, name: String!, registration: String! ): Aircraft
|
||||
@create(model:"GermanAirlinesVa\\Fleet\\Models\\Aircraft")
|
||||
@broadcast(subscription: "aircraftAdded")
|
||||
|
||||
type Subscription {
|
||||
aircraft: [Aircraft] @subscription(class: "GermanAirlinesVa\\Fleet\\Classes\\Aircraft")
|
||||
}
|
||||
|
||||
type Aircraft {
|
||||
id: ID!
|
||||
aircraft_type_id: ID!
|
||||
home_airport_id: ID!
|
||||
name: String!
|
||||
registration: String!
|
||||
}
|
||||
type AircraftType {
|
||||
type: String!
|
||||
aircrafts: [Aircraft] @hasMany
|
||||
}
|
||||
type Aircraft { name: String! registration: String! }
|
||||
type AircraftType { type: String! aircrafts: [Aircraft] @hasMany }
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
==
|
||||
<?php
|
||||
function resolveExamMemberRanks($root, $args) {
|
||||
$exams = \GermanAirlinesVa\Schooling\Models\ExamMemberRank::where('member_id', $args['memberID'])->get();
|
||||
foreach($exams as $exam) {
|
||||
$exam->load([
|
||||
'exam_questions' => function ($query) {
|
||||
$query->groupBy('id');
|
||||
}
|
||||
]);
|
||||
}
|
||||
return $exams;
|
||||
}
|
||||
|
||||
function resolveExamTyperatings($root, $args) {
|
||||
$exams = \GermanAirlinesVa\Schooling\Models\ExamTyperating::where('member_id', $args['memberID'])->get();
|
||||
foreach($exams as $exam) {
|
||||
$exam->load([
|
||||
'exam_questions' => function ($query) {
|
||||
$query->groupBy('id');
|
||||
}
|
||||
]);
|
||||
}
|
||||
return $exams;
|
||||
}
|
||||
|
||||
function resolveTyperatings() {
|
||||
$typeratings = GermanAirlinesVa\Schooling\Models\Typerating::all();
|
||||
foreach($typeratings as $typerating) {
|
||||
$typerating->load([
|
||||
'exam_questions' => function($query) {
|
||||
$query->groupBy('id');
|
||||
}
|
||||
]);
|
||||
}
|
||||
return $typeratings;
|
||||
}
|
||||
|
||||
function resolveMemberRanks() {
|
||||
$ranks = GermanAirlinesVa\Schooling\Models\MemberRank::all();
|
||||
foreach($ranks as $rank) {
|
||||
$rank->load([
|
||||
'exam_questions' => function($query) {
|
||||
$query->groupBy('id');
|
||||
}
|
||||
]);
|
||||
}
|
||||
return $rank;
|
||||
}
|
||||
|
||||
function createExamMemberRank($root, $args) {
|
||||
return GermanAirlinesVa\Schooling\Models\ExamMemberRank::createNew($args['memberID'], $args['memberRankID']);
|
||||
}
|
||||
|
||||
function resolveCreateExamTyperating($root, $args) {
|
||||
return GermanAirlinesVa\Schooling\Models\ExamTyperating::createNew($args['memberID'], $args['typeratingID']);
|
||||
}
|
||||
==
|
||||
# Content
|
||||
extend type Query {
|
||||
memberRanks: [MemberRank]!
|
||||
typeratings: [Typerating]!
|
||||
examMemberRanks(memberID: ID!): [ExamMemberRank]!
|
||||
examTyperatings(memberID: ID!): [ExamTyperating]!
|
||||
}
|
||||
extend type Mutation {
|
||||
createExamMemberRank(memberID: ID!, memberRankID: ID!): ExamMemberRank!
|
||||
createExamTyperating(memberID: ID!, typeratingID: ID!): ExamTyperating!
|
||||
}
|
||||
|
||||
type MemberRank {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
points: Int!
|
||||
price: Int!
|
||||
badge: String!
|
||||
region: String!
|
||||
exam_questions: [ExamMemberRankQuestion]! @hasMany
|
||||
exams: [ExamMemberRank]! @hasMany
|
||||
}
|
||||
type Typerating {
|
||||
id: ID!
|
||||
name: String!
|
||||
price: Int!
|
||||
exam_questions: [ExamTyperatingQuestion]! @hasMany
|
||||
exams: [ExamTyperating]! @hasMany
|
||||
}
|
||||
type ExamMemberRank {
|
||||
id: ID!
|
||||
member_id: ID!
|
||||
member_rank: MemberRank! @belongsTo
|
||||
start: DateTime!
|
||||
status: ExamStatus!
|
||||
exam_questions: [ExamMemberRankQuestion]! @belongsToMany
|
||||
exam_answers: [ExamMemberRankAnswer]! @belongsToMany
|
||||
}
|
||||
type ExamTyperating {
|
||||
id: ID!
|
||||
member_id: ID!
|
||||
typerating: Typerating! @belongsTo
|
||||
start: DateTime!
|
||||
status: ExamStatus!
|
||||
exam_questions: [ExamTyperatingQuestion]! @belongsToMany
|
||||
exam_answers: [ExamTyperatingAnswer]! @belongsToMany
|
||||
}
|
||||
type ExamMemberRankQuestion {
|
||||
id: ID!
|
||||
member_rank: MemberRank! @belongsTo
|
||||
in_use: Boolean!
|
||||
mandatory: Boolean!
|
||||
text: String!
|
||||
picture: String!
|
||||
exam_answers: [ExamMemberRankAnswer]! @hasMany
|
||||
exam_member_ranks: [ExamMemberRank]! @belongsToMany
|
||||
}
|
||||
type ExamTyperatingQuestion {
|
||||
id: ID!
|
||||
typerating: Typerating! @belongsTo
|
||||
in_use: Boolean!
|
||||
mandatory: Boolean!
|
||||
text: String!
|
||||
picture: String!
|
||||
exam_answers: [ExamTyperatingAnswer]! @hasMany
|
||||
exma_typeratings: [ExamTyperating]! @belongsToMany
|
||||
}
|
||||
type ExamMemberRankAnswer {
|
||||
id: ID!
|
||||
exam_question: ExamMemberRankQuestion! @belongsTo
|
||||
text: String!
|
||||
is_correct: Boolean!
|
||||
exam_member_ranks: [ExamMemberRank]! @belongsToMany
|
||||
}
|
||||
type ExamTyperatingAnswer {
|
||||
id: ID!
|
||||
exam_question: ExamTyperatingQuestion! @belongsTo
|
||||
text: String!
|
||||
is_correct: Boolean!
|
||||
exam_typeratings: [ExamTyperating]! @belongsToMany
|
||||
}
|
||||
|
||||
enum ExamStatus {
|
||||
open
|
||||
pending
|
||||
closed
|
||||
validated
|
||||
}
|
||||
|
||||
scalar DateTime
|
||||
@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime")
|
||||
+1
-3
@@ -4,9 +4,7 @@
|
||||
'description' => '',
|
||||
],
|
||||
'menu' => [
|
||||
'main' => 'GA GraphQL',
|
||||
'keys' => 'GraphQL Keys',
|
||||
'playground' => 'GraphiQL',
|
||||
'playground' => 'GA GraphiQL',
|
||||
],
|
||||
'permission' => [
|
||||
'tab' => [
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php namespace GermanAirlinesVa\Graphql\Models;
|
||||
|
||||
use Model;
|
||||
|
||||
class GraphqlKey extends Model
|
||||
{
|
||||
use \October\Rain\Database\Traits\Validation;
|
||||
|
||||
/*
|
||||
* Disable timestamps by default.
|
||||
* Remove this line if timestamps are defined in the database table.
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
public $table = 'graphql_keys';
|
||||
protected $connection = 'germanairlinesva_graphql';
|
||||
|
||||
/**
|
||||
* @var array Validation rules
|
||||
*/
|
||||
public $rules = [
|
||||
'member_id' => 'required',
|
||||
'key' => 'required',
|
||||
];
|
||||
|
||||
public $belongsTo = [
|
||||
'member' => 'GermanAirlinesVa\Social\Models\Member',
|
||||
];
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php namespace GermanAirlinesVa\Graphql\Models;
|
||||
|
||||
use Cms\Classes\Theme;
|
||||
use Model;
|
||||
|
||||
class Settings extends Model
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
columns:
|
||||
id:
|
||||
label: id
|
||||
type: number
|
||||
member:
|
||||
label: member
|
||||
type: text
|
||||
relation: member
|
||||
valueFrom: name
|
||||
valid_from:
|
||||
label: valid_from
|
||||
type: datetime
|
||||
key:
|
||||
label: key
|
||||
type: text
|
||||
+3
-4
@@ -6,11 +6,10 @@
|
||||
"author": "German Airlines VA",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@prettier/plugin-php": "^0.17.3",
|
||||
"prettier": "^2.3.2"
|
||||
"@prettier/plugin-php": "^0.16.3",
|
||||
"prettier": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "git config core.hooksPath .hooks",
|
||||
"format": "prettier --write './**/*.{php,html,yaml}'"
|
||||
"format": "prettier --write './**/*.{php,html,htm,yaml}'"
|
||||
}
|
||||
}
|
||||
|
||||
+4
-13
@@ -5,22 +5,13 @@ plugin:
|
||||
icon: oc-icon-database
|
||||
homepage: ''
|
||||
permissions:
|
||||
germanairlinesva.graphql.master:
|
||||
schemas:
|
||||
tab: 'germanairlinesva.graphql::lang.permission.tab.schemas'
|
||||
label: 'germanairlinesva.graphql::lang.permission.label.schemas'
|
||||
navigation:
|
||||
menu:
|
||||
label: 'germanairlinesva.graphql::lang.menu.main'
|
||||
url: /
|
||||
label: 'germanairlinesva.graphql::lang.menu.playground'
|
||||
url: germanairlinesva/graphql/playground
|
||||
icon: icon-database
|
||||
permissions:
|
||||
- germanairlinesva.graphql.master
|
||||
sideMenu:
|
||||
side-menu-item:
|
||||
label: 'germanairlinesva.graphql::lang.menu.playground'
|
||||
url: germanairlinesva/graphql/playground
|
||||
icon: icon-database
|
||||
side-menu-item2:
|
||||
label: 'germanairlinesva.graphql::lang.menu.keys'
|
||||
url: germanairlinesva/graphql/graphqlkey
|
||||
icon: icon-key
|
||||
- schemas
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php namespace GermanAirlinesVa\GraphQl\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class BuilderTableCreateGermanAirlinesVaGraphQlDeferredBindings extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::connection('germanairlinesva_graphql')->create('deferred_bindings', function ($table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id')->unsigned();
|
||||
$table->string('master_type');
|
||||
$table->string('master_field');
|
||||
$table->string('slave_type');
|
||||
$table->integer('slave_id');
|
||||
$table->mediumText('pivot_data')->nullable();
|
||||
$table->string('session_key');
|
||||
$table->boolean('is_bind')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::connection('germanairlinesva_graphql')->disableForeignKeyConstraints();
|
||||
Schema::connection('germanairlinesva_graphql')->dropIfExists('deferred_bindings');
|
||||
Schema::connection('germanairlinesva_graphql')->enableForeignKeyConstraints();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php namespace GermanAirlinesVa\Graphql\Updates;
|
||||
|
||||
use DB;
|
||||
use Schema;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class BuilderTableCreateGermanAirlinesVaGraphqlGraphqlKeys extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::connection('germanairlinesva_graphql')->create('graphql_keys', function ($table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->bigIncrements('id')->unsigned();
|
||||
$table->bigInteger('member_id')->unsigned();
|
||||
$table->datetime('valid_from')->default(DB::raw('NOW()'));
|
||||
$table->string('key');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::connection('germanairlinesva_graphql')->disableForeignKeyConstraints();
|
||||
Schema::connection('germanairlinesva_graphql')->dropIfExists('graphql_keys');
|
||||
Schema::connection('germanairlinesva_graphql')->enableForeignKeyConstraints();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,2 @@
|
||||
1.0.1:
|
||||
- 'Initialize plugin.'
|
||||
- 'Create table deferred_bindings'
|
||||
- builder_table_create_deferred_bindings.php
|
||||
1.0.2:
|
||||
- 'Create table graphql_keys'
|
||||
- builder_table_create_graphql_keys.php
|
||||
- Initialize plugin.
|
||||
|
||||
+4
@@ -6,7 +6,11 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
|
||||
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
|
||||
Vendored
+15
-4
@@ -6,6 +6,20 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
||||
'72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'538ca81a9a966a6716601ecf48f4eaef' => $vendorDir . '/opis/closure/functions.php',
|
||||
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
|
||||
'51fcf4e06c07cc00c920b44bcd900e7a' => $vendorDir . '/thecodingmachine/safe/deprecated/apc.php',
|
||||
'47f619d9197b36cf5ab70738d7743fe2' => $vendorDir . '/thecodingmachine/safe/deprecated/libevent.php',
|
||||
'ea6bb8a12ef9b68f6ada99058e530760' => $vendorDir . '/thecodingmachine/safe/deprecated/mssql.php',
|
||||
@@ -95,9 +109,6 @@ return array(
|
||||
'4af1dca6db8c527c6eed27bff85ff0e5' => $vendorDir . '/thecodingmachine/safe/generated/yaz.php',
|
||||
'fe43ca06499ac37bc2dedd823af71eb5' => $vendorDir . '/thecodingmachine/safe/generated/zip.php',
|
||||
'356736db98a6834f0a886b8d509b0ecd' => $vendorDir . '/thecodingmachine/safe/generated/zlib.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
);
|
||||
|
||||
Vendored
+45
@@ -6,19 +6,64 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'voku\\' => array($vendorDir . '/voku/portable-ascii/src/voku'),
|
||||
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
||||
'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'),
|
||||
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
|
||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
|
||||
'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'),
|
||||
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
|
||||
'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
|
||||
'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
|
||||
'Symfony\\Contracts\\HttpClient\\' => array($vendorDir . '/symfony/http-client-contracts'),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'),
|
||||
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
|
||||
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
|
||||
'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'),
|
||||
'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
|
||||
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
|
||||
'Symfony\\Component\\Mime\\' => array($vendorDir . '/symfony/mime'),
|
||||
'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
|
||||
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
|
||||
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
|
||||
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
|
||||
'Symfony\\Component\\ErrorHandler\\' => array($vendorDir . '/symfony/error-handler'),
|
||||
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
|
||||
'Safe\\' => array($vendorDir . '/thecodingmachine/safe/lib', $vendorDir . '/thecodingmachine/safe/deprecated', $vendorDir . '/thecodingmachine/safe/generated'),
|
||||
'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
|
||||
'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'),
|
||||
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
|
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||
'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
|
||||
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
|
||||
'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'),
|
||||
'Nuwave\\Lighthouse\\' => array($vendorDir . '/nuwave/lighthouse/src'),
|
||||
'Laragraph\\Utils\\' => array($vendorDir . '/laragraph/utils/src'),
|
||||
'Illuminate\\Validation\\' => array($vendorDir . '/illuminate/validation'),
|
||||
'Illuminate\\Translation\\' => array($vendorDir . '/illuminate/translation'),
|
||||
'Illuminate\\Support\\' => array($vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/support'),
|
||||
'Illuminate\\Session\\' => array($vendorDir . '/illuminate/session'),
|
||||
'Illuminate\\Routing\\' => array($vendorDir . '/illuminate/routing'),
|
||||
'Illuminate\\Queue\\' => array($vendorDir . '/illuminate/queue'),
|
||||
'Illuminate\\Pipeline\\' => array($vendorDir . '/illuminate/pipeline'),
|
||||
'Illuminate\\Pagination\\' => array($vendorDir . '/illuminate/pagination'),
|
||||
'Illuminate\\Http\\' => array($vendorDir . '/illuminate/http'),
|
||||
'Illuminate\\Filesystem\\' => array($vendorDir . '/illuminate/filesystem'),
|
||||
'Illuminate\\Database\\' => array($vendorDir . '/illuminate/database'),
|
||||
'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
|
||||
'Illuminate\\Container\\' => array($vendorDir . '/illuminate/container'),
|
||||
'Illuminate\\Console\\' => array($vendorDir . '/illuminate/console'),
|
||||
'Illuminate\\Bus\\' => array($vendorDir . '/illuminate/bus'),
|
||||
'Illuminate\\Auth\\' => array($vendorDir . '/illuminate/auth'),
|
||||
'HaydenPierce\\ClassFinder\\UnitTest\\' => array($vendorDir . '/haydenpierce/class-finder/test/unit'),
|
||||
'HaydenPierce\\ClassFinder\\' => array($vendorDir . '/haydenpierce/class-finder/src'),
|
||||
'GraphQL\\' => array($vendorDir . '/webonyx/graphql-php/src'),
|
||||
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
|
||||
'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
|
||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
|
||||
'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'),
|
||||
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
|
||||
'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
|
||||
);
|
||||
|
||||
Vendored
+270
-4
@@ -7,6 +7,20 @@ namespace Composer\Autoload;
|
||||
class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
{
|
||||
public static $files = array (
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
||||
'72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'538ca81a9a966a6716601ecf48f4eaef' => __DIR__ . '/..' . '/opis/closure/functions.php',
|
||||
'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
|
||||
'51fcf4e06c07cc00c920b44bcd900e7a' => __DIR__ . '/..' . '/thecodingmachine/safe/deprecated/apc.php',
|
||||
'47f619d9197b36cf5ab70738d7743fe2' => __DIR__ . '/..' . '/thecodingmachine/safe/deprecated/libevent.php',
|
||||
'ea6bb8a12ef9b68f6ada99058e530760' => __DIR__ . '/..' . '/thecodingmachine/safe/deprecated/mssql.php',
|
||||
@@ -96,23 +110,48 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
'4af1dca6db8c527c6eed27bff85ff0e5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/yaz.php',
|
||||
'fe43ca06499ac37bc2dedd823af71eb5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zip.php',
|
||||
'356736db98a6834f0a886b8d509b0ecd' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zlib.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'v' =>
|
||||
array (
|
||||
'voku\\' => 5,
|
||||
),
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Php80\\' => 23,
|
||||
'Symfony\\Polyfill\\Php73\\' => 23,
|
||||
'Symfony\\Polyfill\\Php72\\' => 23,
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
||||
'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31,
|
||||
'Symfony\\Polyfill\\Ctype\\' => 23,
|
||||
'Symfony\\Contracts\\Translation\\' => 30,
|
||||
'Symfony\\Contracts\\Service\\' => 26,
|
||||
'Symfony\\Contracts\\HttpClient\\' => 29,
|
||||
'Symfony\\Contracts\\EventDispatcher\\' => 34,
|
||||
'Symfony\\Component\\VarDumper\\' => 28,
|
||||
'Symfony\\Component\\Translation\\' => 30,
|
||||
'Symfony\\Component\\String\\' => 25,
|
||||
'Symfony\\Component\\Routing\\' => 26,
|
||||
'Symfony\\Component\\Process\\' => 26,
|
||||
'Symfony\\Component\\Mime\\' => 23,
|
||||
'Symfony\\Component\\HttpKernel\\' => 29,
|
||||
'Symfony\\Component\\HttpFoundation\\' => 33,
|
||||
'Symfony\\Component\\Finder\\' => 25,
|
||||
'Symfony\\Component\\EventDispatcher\\' => 34,
|
||||
'Symfony\\Component\\ErrorHandler\\' => 31,
|
||||
'Symfony\\Component\\Console\\' => 26,
|
||||
'Safe\\' => 5,
|
||||
),
|
||||
'R' =>
|
||||
array (
|
||||
'Ramsey\\Uuid\\' => 12,
|
||||
'Ramsey\\Collection\\' => 18,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\SimpleCache\\' => 16,
|
||||
@@ -120,6 +159,10 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
'Psr\\EventDispatcher\\' => 20,
|
||||
'Psr\\Container\\' => 14,
|
||||
),
|
||||
'O' =>
|
||||
array (
|
||||
'Opis\\Closure\\' => 13,
|
||||
),
|
||||
'N' =>
|
||||
array (
|
||||
'Nuwave\\Lighthouse\\' => 18,
|
||||
@@ -128,6 +171,25 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
array (
|
||||
'Laragraph\\Utils\\' => 16,
|
||||
),
|
||||
'I' =>
|
||||
array (
|
||||
'Illuminate\\Validation\\' => 22,
|
||||
'Illuminate\\Translation\\' => 23,
|
||||
'Illuminate\\Support\\' => 19,
|
||||
'Illuminate\\Session\\' => 19,
|
||||
'Illuminate\\Routing\\' => 19,
|
||||
'Illuminate\\Queue\\' => 17,
|
||||
'Illuminate\\Pipeline\\' => 20,
|
||||
'Illuminate\\Pagination\\' => 22,
|
||||
'Illuminate\\Http\\' => 16,
|
||||
'Illuminate\\Filesystem\\' => 22,
|
||||
'Illuminate\\Database\\' => 20,
|
||||
'Illuminate\\Contracts\\' => 21,
|
||||
'Illuminate\\Container\\' => 21,
|
||||
'Illuminate\\Console\\' => 19,
|
||||
'Illuminate\\Bus\\' => 15,
|
||||
'Illuminate\\Auth\\' => 16,
|
||||
),
|
||||
'H' =>
|
||||
array (
|
||||
'HaydenPierce\\ClassFinder\\UnitTest\\' => 34,
|
||||
@@ -137,9 +199,35 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
array (
|
||||
'GraphQL\\' => 8,
|
||||
),
|
||||
'E' =>
|
||||
array (
|
||||
'Egulias\\EmailValidator\\' => 23,
|
||||
),
|
||||
'D' =>
|
||||
array (
|
||||
'Doctrine\\Inflector\\' => 19,
|
||||
'Doctrine\\Common\\Lexer\\' => 22,
|
||||
),
|
||||
'C' =>
|
||||
array (
|
||||
'Composer\\Installers\\' => 20,
|
||||
'Carbon\\' => 7,
|
||||
),
|
||||
'B' =>
|
||||
array (
|
||||
'Brick\\Math\\' => 11,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'voku\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/voku/portable-ascii/src/voku',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php80\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php73\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php73',
|
||||
@@ -148,6 +236,10 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
|
||||
),
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||
),
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
|
||||
@@ -160,12 +252,88 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme',
|
||||
),
|
||||
'Symfony\\Polyfill\\Ctype\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
||||
),
|
||||
'Symfony\\Contracts\\Translation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/translation-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\Service\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/service-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\HttpClient\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/http-client-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts',
|
||||
),
|
||||
'Symfony\\Component\\VarDumper\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/var-dumper',
|
||||
),
|
||||
'Symfony\\Component\\Translation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/translation',
|
||||
),
|
||||
'Symfony\\Component\\String\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/string',
|
||||
),
|
||||
'Symfony\\Component\\Routing\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/routing',
|
||||
),
|
||||
'Symfony\\Component\\Process\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/process',
|
||||
),
|
||||
'Symfony\\Component\\Mime\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/mime',
|
||||
),
|
||||
'Symfony\\Component\\HttpKernel\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/http-kernel',
|
||||
),
|
||||
'Symfony\\Component\\HttpFoundation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/http-foundation',
|
||||
),
|
||||
'Symfony\\Component\\Finder\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/finder',
|
||||
),
|
||||
'Symfony\\Component\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
|
||||
),
|
||||
'Symfony\\Component\\ErrorHandler\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/error-handler',
|
||||
),
|
||||
'Symfony\\Component\\Console\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/console',
|
||||
),
|
||||
'Safe\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/thecodingmachine/safe/lib',
|
||||
1 => __DIR__ . '/..' . '/thecodingmachine/safe/deprecated',
|
||||
2 => __DIR__ . '/..' . '/thecodingmachine/safe/generated',
|
||||
),
|
||||
'Ramsey\\Uuid\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ramsey/uuid/src',
|
||||
),
|
||||
'Ramsey\\Collection\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ramsey/collection/src',
|
||||
),
|
||||
'Psr\\SimpleCache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
|
||||
@@ -182,6 +350,10 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/container/src',
|
||||
),
|
||||
'Opis\\Closure\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/opis/closure/src',
|
||||
),
|
||||
'Nuwave\\Lighthouse\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nuwave/lighthouse/src',
|
||||
@@ -190,6 +362,72 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/laragraph/utils/src',
|
||||
),
|
||||
'Illuminate\\Validation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/validation',
|
||||
),
|
||||
'Illuminate\\Translation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/translation',
|
||||
),
|
||||
'Illuminate\\Support\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/macroable',
|
||||
1 => __DIR__ . '/..' . '/illuminate/collections',
|
||||
2 => __DIR__ . '/..' . '/illuminate/support',
|
||||
),
|
||||
'Illuminate\\Session\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/session',
|
||||
),
|
||||
'Illuminate\\Routing\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/routing',
|
||||
),
|
||||
'Illuminate\\Queue\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/queue',
|
||||
),
|
||||
'Illuminate\\Pipeline\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/pipeline',
|
||||
),
|
||||
'Illuminate\\Pagination\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/pagination',
|
||||
),
|
||||
'Illuminate\\Http\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/http',
|
||||
),
|
||||
'Illuminate\\Filesystem\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/filesystem',
|
||||
),
|
||||
'Illuminate\\Database\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/database',
|
||||
),
|
||||
'Illuminate\\Contracts\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/contracts',
|
||||
),
|
||||
'Illuminate\\Container\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/container',
|
||||
),
|
||||
'Illuminate\\Console\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/console',
|
||||
),
|
||||
'Illuminate\\Bus\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/bus',
|
||||
),
|
||||
'Illuminate\\Auth\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/illuminate/auth',
|
||||
),
|
||||
'HaydenPierce\\ClassFinder\\UnitTest\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/haydenpierce/class-finder/test/unit',
|
||||
@@ -202,12 +440,40 @@ class ComposerStaticInit02b791b67b928853969b061eb6816088
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/webonyx/graphql-php/src',
|
||||
),
|
||||
'Egulias\\EmailValidator\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/egulias/email-validator/src',
|
||||
),
|
||||
'Doctrine\\Inflector\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Inflector',
|
||||
),
|
||||
'Doctrine\\Common\\Lexer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',
|
||||
),
|
||||
'Composer\\Installers\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers',
|
||||
),
|
||||
'Carbon\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
|
||||
),
|
||||
'Brick\\Math\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/brick/math/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
|
||||
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
|
||||
Vendored
+18
-18
@@ -2963,22 +2963,22 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v5.3.4",
|
||||
"version_normalized": "5.3.4.0",
|
||||
"version": "v5.3.3",
|
||||
"version_normalized": "5.3.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "38a768f2946a7261dfc399873dda2c115dd6ac17"
|
||||
"reference": "90ad9f4b21ddcb8ebe9faadfcca54929ad23f9f8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/38a768f2946a7261dfc399873dda2c115dd6ac17",
|
||||
"reference": "38a768f2946a7261dfc399873dda2c115dd6ac17",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/90ad9f4b21ddcb8ebe9faadfcca54929ad23f9f8",
|
||||
"reference": "90ad9f4b21ddcb8ebe9faadfcca54929ad23f9f8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"psr/log": "^1|^2",
|
||||
"psr/log": "~1.0",
|
||||
"symfony/deprecation-contracts": "^2.1",
|
||||
"symfony/error-handler": "^4.4|^5.0",
|
||||
"symfony/event-dispatcher": "^5.0",
|
||||
@@ -2986,7 +2986,7 @@
|
||||
"symfony/http-foundation": "^5.3",
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-php73": "^1.9",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
"symfony/polyfill-php80": "^1.15"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/browser-kit": "<4.4",
|
||||
@@ -3005,7 +3005,7 @@
|
||||
"twig/twig": "<2.13"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0|2.0"
|
||||
"psr/log-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/cache": "^1.0|^2.0|^3.0",
|
||||
@@ -3030,7 +3030,7 @@
|
||||
"symfony/console": "",
|
||||
"symfony/dependency-injection": ""
|
||||
},
|
||||
"time": "2021-07-26T17:36:49+00:00",
|
||||
"time": "2021-06-30T08:27:49+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
@@ -3058,7 +3058,7 @@
|
||||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v5.3.4"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v5.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3837,24 +3837,24 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v5.3.4",
|
||||
"version_normalized": "5.3.4.0",
|
||||
"version": "v5.3.2",
|
||||
"version_normalized": "5.3.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "d16634ee55b895bd85ec714dadc58e4428ecf030"
|
||||
"reference": "714b47f9196de61a196d86c4bad5f09201b307df"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/d16634ee55b895bd85ec714dadc58e4428ecf030",
|
||||
"reference": "d16634ee55b895bd85ec714dadc58e4428ecf030",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/714b47f9196de61a196d86c4bad5f09201b307df",
|
||||
"reference": "714b47f9196de61a196d86c4bad5f09201b307df",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/polyfill-php80": "^1.16"
|
||||
"symfony/polyfill-php80": "^1.15"
|
||||
},
|
||||
"time": "2021-07-23T15:54:19+00:00",
|
||||
"time": "2021-06-12T10:15:01+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
@@ -3882,7 +3882,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v5.3.4"
|
||||
"source": "https://github.com/symfony/process/tree/v5.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 70200)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/ci/ export-ignore
|
||||
/docs/ export-ignore
|
||||
/test/ export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.gitlab-ci.yml export-ignore
|
||||
/composer.lock export-ignore
|
||||
/phpunit.xml export-ignore
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
Copyright (c) 2018 Hayden Pierce
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
ClassFinder
|
||||
===========
|
||||
|
||||
A dead simple utility to identify classes in a given namespace.
|
||||
|
||||
This package is an improved implementation of an [answer on Stack Overflow](https://stackoverflow.com/a/40229665/3000068)
|
||||
and provides additional features with less configuration required.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* Application is using Composer.
|
||||
* Classes can be autoloaded with Composer.
|
||||
* PHP >= 5.3.0
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Installing is done by requiring it with Composer.
|
||||
|
||||
```
|
||||
$ composer require haydenpierce/class-finder
|
||||
```
|
||||
|
||||
No other installation methods are currently supported.
|
||||
|
||||
Supported Autoloading Methods
|
||||
--------------------------------
|
||||
|
||||
| Method | Supported | with `ClassFinder::RECURSIVE_MODE` |
|
||||
| ---------- | --------- | ---------------------------------- |
|
||||
| PSR-4 | ✔️ | ✔️ |
|
||||
| PSR-0 | ❌️* | ❌️* |
|
||||
| Classmap | ✔️ | ✔️ |
|
||||
| Files | ✔️^ | ❌️** |
|
||||
|
||||
\^ Experimental.
|
||||
|
||||
\* Planned.
|
||||
|
||||
\** Not planned. Open an issue if you need this feature.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
**Standard Mode**
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$classes = ClassFinder::getClassesInNamespace('TestApp1\Foo');
|
||||
|
||||
/**
|
||||
* array(
|
||||
* 'TestApp1\Foo\Bar',
|
||||
* 'TestApp1\Foo\Baz',
|
||||
* 'TestApp1\Foo\Foo'
|
||||
* )
|
||||
*/
|
||||
var_dump($classes);
|
||||
```
|
||||
|
||||
**Recursive Mode** *(in v0.3-beta)*
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$classes = ClassFinder::getClassesInNamespace('TestApp1\Foo', ClassFinder::RECURSIVE_MODE);
|
||||
|
||||
/**
|
||||
* array(
|
||||
* 'TestApp1\Foo\Bar',
|
||||
* 'TestApp1\Foo\Baz',
|
||||
* 'TestApp1\Foo\Foo',
|
||||
* 'TestApp1\Foo\Box\Bar',
|
||||
* 'TestApp1\Foo\Box\Baz',
|
||||
* 'TestApp1\Foo\Box\Foo',
|
||||
* 'TestApp1\Foo\Box\Lon\Bar',
|
||||
* 'TestApp1\Foo\Box\Lon\Baz',
|
||||
* 'TestApp1\Foo\Box\Lon\Foo',
|
||||
* )
|
||||
*/
|
||||
var_dump($classes);
|
||||
```
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
[Changelog](docs/changelog.md)
|
||||
|
||||
**Exceptions**:
|
||||
|
||||
* [Files could not locate PHP](docs/exceptions/filesCouldNotLocatePHP.md)
|
||||
* [Files exec not available](docs/exceptions/filesExecNotAvailable.md)
|
||||
* [Missing composer.json](docs/exceptions/missingComposerConfig.md)
|
||||
|
||||
**Internals**
|
||||
|
||||
* [How Testing Works](docs/testing.md)
|
||||
* [Continuous Integration Notes](docs/ci.md)
|
||||
|
||||
Future Work
|
||||
-----------
|
||||
|
||||
> **WARNING**: Before 1.0.0, expect that bug fixes _will not_ be backported to older versions. Backwards incompatible changes
|
||||
may be introduced in minor 0.X.Y versions, where X changes.
|
||||
|
||||
* `psr0` support
|
||||
|
||||
* Additional features:
|
||||
|
||||
Various ideas:
|
||||
|
||||
* ~~`ClassFinder::getClassesInNamespace('TestApp1\Foo', ClassFinder::RECURSIVE_MODE)`.
|
||||
Providing classes multiple namespaces deep.~~ (included v0.3-beta)
|
||||
|
||||
* `ClassFinder::getClassesImplementingInterface('TestApp1\Foo', 'TestApp1\FooInterface', ClassFinder::RECURSIVE_MODE)`.
|
||||
Filtering classes to only classes that implement a namespace.
|
||||
|
||||
* `ClassFinder::debugRenderReport('TestApp1\Foo\Baz')`
|
||||
Guidance for solving "class not found" errors resulting from typos in namespaces, missing directories, etc. Would print
|
||||
an HTML report. Not intended for production use, but debugging.
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "haydenpierce/class-finder",
|
||||
"description" : "A library that can provide of a list of classes in a given namespace",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"version": "0.4.3",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Hayden Pierce",
|
||||
"email": "hayden@haydenpierce.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~9.0",
|
||||
"mikey179/vfsstream": "^1.6"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"HaydenPierce\\ClassFinder\\": "src/",
|
||||
"HaydenPierce\\ClassFinder\\UnitTest\\": "test/unit"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder;
|
||||
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class AppConfig
|
||||
{
|
||||
/** @var string */
|
||||
private $appRoot;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->appRoot = $this->findAppRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function findAppRoot()
|
||||
{
|
||||
if ($this->appRoot) {
|
||||
$appRoot = $this->appRoot;
|
||||
} else {
|
||||
$workingDirectory = str_replace('\\', '/', __DIR__);
|
||||
$workingDirectory = str_replace('/vendor/haydenpierce/class-finder/src', '', $workingDirectory);
|
||||
$directoryPathPieces = explode('/', $workingDirectory);
|
||||
|
||||
$appRoot = null;
|
||||
do {
|
||||
$path = implode('/', $directoryPathPieces) . '/composer.json';
|
||||
if (file_exists($path)) {
|
||||
$appRoot = implode('/', $directoryPathPieces) . '/';
|
||||
} else {
|
||||
array_pop($directoryPathPieces);
|
||||
}
|
||||
} while (is_null($appRoot) && count($directoryPathPieces) > 0);
|
||||
}
|
||||
|
||||
$this->throwIfInvalidAppRoot($appRoot);
|
||||
|
||||
$this->appRoot= $appRoot;
|
||||
return $this->appRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appRoot
|
||||
* @return void
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
private function throwIfInvalidAppRoot($appRoot)
|
||||
{
|
||||
if (!file_exists($appRoot . '/composer.json')) {
|
||||
throw new ClassFinderException(sprintf("Could not locate composer.json. You can get around this by setting ClassFinder::\$appRoot manually. See '%s' for details.",
|
||||
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/missingComposerConfig.md'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAppRoot()
|
||||
{
|
||||
if ($this->appRoot === null) {
|
||||
$this->appRoot = $this->findAppRoot();
|
||||
}
|
||||
|
||||
$this->throwIfInvalidAppRoot($this->appRoot);
|
||||
|
||||
return $this->appRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appRoot
|
||||
* @return void
|
||||
*/
|
||||
public function setAppRoot($appRoot)
|
||||
{
|
||||
$this->appRoot = $appRoot;
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder;
|
||||
|
||||
use HaydenPierce\ClassFinder\Classmap\ClassmapEntryFactory;
|
||||
use HaydenPierce\ClassFinder\Classmap\ClassmapFinder;
|
||||
use HaydenPierce\ClassFinder\Files\FilesEntryFactory;
|
||||
use HaydenPierce\ClassFinder\Files\FilesFinder;
|
||||
use HaydenPierce\ClassFinder\PSR4\PSR4Finder;
|
||||
use HaydenPierce\ClassFinder\PSR4\PSR4NamespaceFactory;
|
||||
|
||||
class ClassFinder
|
||||
{
|
||||
const STANDARD_MODE = 1;
|
||||
const RECURSIVE_MODE = 2;
|
||||
|
||||
/** @var AppConfig */
|
||||
private static $config;
|
||||
|
||||
/** @var PSR4Finder */
|
||||
private static $psr4;
|
||||
|
||||
/** @var ClassmapFinder */
|
||||
private static $classmap;
|
||||
|
||||
/** @var FilesFinder */
|
||||
private static $files;
|
||||
|
||||
/** @var boolean */
|
||||
private static $useFilesSupport = false;
|
||||
|
||||
/** @var boolean */
|
||||
private static $usePSR4Support = true;
|
||||
|
||||
/** @var boolean */
|
||||
private static $useClassmapSupport = true;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initialize()
|
||||
{
|
||||
if (!(self::$config instanceof AppConfig)) {
|
||||
self::$config = new AppConfig();
|
||||
}
|
||||
|
||||
if (!(self::$psr4 instanceof PSR4Finder)) {
|
||||
$PSR4Factory = new PSR4NamespaceFactory(self::$config);
|
||||
self::$psr4 = new PSR4Finder($PSR4Factory);
|
||||
}
|
||||
|
||||
if (!(self::$classmap instanceof ClassmapFinder)) {
|
||||
$classmapFactory = new ClassmapEntryFactory(self::$config);
|
||||
self::$classmap = new ClassmapFinder($classmapFactory);
|
||||
}
|
||||
|
||||
if (!(self::$files instanceof FilesFinder) && self::$useFilesSupport) {
|
||||
$filesFactory = new FilesEntryFactory(self::$config);
|
||||
self::$files = new FilesFinder($filesFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify classes in a given namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getClassesInNamespace($namespace, $options = self::STANDARD_MODE)
|
||||
{
|
||||
self::initialize();
|
||||
|
||||
$findersWithNamespace = self::findersWithNamespace($namespace);
|
||||
|
||||
$classes = array_reduce($findersWithNamespace, function($carry, FinderInterface $finder) use ($namespace, $options){
|
||||
return array_merge($carry, $finder->findClasses($namespace, $options));
|
||||
}, array());
|
||||
|
||||
return array_unique($classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given namespace contains any classes.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public static function namespaceHasClasses($namespace)
|
||||
{
|
||||
self::initialize();
|
||||
|
||||
return count(self::findersWithNamespace($namespace)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $appRoot
|
||||
* @return void
|
||||
*/
|
||||
public static function setAppRoot($appRoot)
|
||||
{
|
||||
self::initialize();
|
||||
self::$config->setAppRoot($appRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function enableExperimentalFilesSupport()
|
||||
{
|
||||
self::$useFilesSupport = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function disableExperimentalFilesSupport()
|
||||
{
|
||||
self::$useFilesSupport = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function enablePSR4Support()
|
||||
{
|
||||
self::$usePSR4Support = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function disablePSR4Support()
|
||||
{
|
||||
self::$usePSR4Support = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function enableClassmapSupport()
|
||||
{
|
||||
self::$useClassmapSupport = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function disableClassmapSupport()
|
||||
{
|
||||
self::$useClassmapSupport = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FinderInterface[]
|
||||
*/
|
||||
private static function getSupportedFinders()
|
||||
{
|
||||
$supportedFinders = array();
|
||||
|
||||
/*
|
||||
* This is done for testing. For some tests, allowing PSR4 classes contaminates the test results. This could also be
|
||||
* disabled for performance reasons (less finders in use means less work), but most people probably won't do that.
|
||||
*/
|
||||
if (self::$usePSR4Support) {
|
||||
$supportedFinders[] = self::$psr4;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is done for testing. For some tests, allowing classmap classes contaminates the test results. This could also be
|
||||
* disabled for performance reasons (less finders in use means less work), but most people probably won't do that.
|
||||
*/
|
||||
if (self::$useClassmapSupport) {
|
||||
$supportedFinders[] = self::$classmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Files support is tucked away behind a flag because it will need to use some kind of shell access via exec, or
|
||||
* system.
|
||||
*
|
||||
* #1 Many environments (such as shared space hosts) may not allow these functions, and attempting to call
|
||||
* these functions will blow up.
|
||||
* #2 I've heard of performance issues with calling these functions.
|
||||
* #3 Files support probably doesn't benefit most projects.
|
||||
* #4 Using exec() or system() is against many PHP developers' religions.
|
||||
*/
|
||||
if (self::$useFilesSupport) {
|
||||
$supportedFinders[] = self::$files;
|
||||
}
|
||||
|
||||
return $supportedFinders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return FinderInterface[]
|
||||
*/
|
||||
private static function findersWithNamespace($namespace)
|
||||
{
|
||||
$findersWithNamespace = array_filter(self::getSupportedFinders(), function (FinderInterface $finder) use ($namespace) {
|
||||
return $finder->isNamespaceKnown($namespace);
|
||||
});
|
||||
|
||||
return $findersWithNamespace;
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
|
||||
class ClassmapEntry
|
||||
{
|
||||
/** @var string */
|
||||
private $className;
|
||||
|
||||
/**
|
||||
* @param string $fullyQualifiedClassName
|
||||
*/
|
||||
public function __construct($fullyQualifiedClassName)
|
||||
{
|
||||
$this->className = $fullyQualifiedClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function knowsNamespace($namespace)
|
||||
{
|
||||
return strpos($this->className, $namespace) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function matches($namespace, $options)
|
||||
{
|
||||
if ($options === ClassFinder::RECURSIVE_MODE) {
|
||||
return $this->doesMatchAnyNamespace($namespace);
|
||||
} else {
|
||||
return $this->doesMatchDirectNamespace($namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getClassName()
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the class is a child or subchild of the given namespace.
|
||||
*
|
||||
* @param $namespace
|
||||
* @return bool
|
||||
*/
|
||||
private function doesMatchAnyNamespace($namespace)
|
||||
{
|
||||
return strpos($this->getClassName(),$namespace) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the class is a DIRECT child of the given namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
private function doesMatchDirectNamespace($namespace)
|
||||
{
|
||||
$classNameFragments = explode('\\', $this->getClassName());
|
||||
array_pop($classNameFragments);
|
||||
$classNamespace = implode('\\', $classNameFragments);
|
||||
|
||||
$namespace = trim($namespace, '\\');
|
||||
|
||||
return $namespace === $classNamespace;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\AppConfig;
|
||||
|
||||
class ClassmapEntryFactory
|
||||
{
|
||||
/** @var AppConfig */
|
||||
private $appConfig;
|
||||
|
||||
public function __construct(AppConfig $appConfig)
|
||||
{
|
||||
$this->appConfig = $appConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassmapEntry[]
|
||||
*/
|
||||
public function getClassmapEntries()
|
||||
{
|
||||
// Composer will compile user declared mappings to autoload_classmap.php. So no additional work is needed
|
||||
// to fetch user provided entries.
|
||||
$classmap = require($this->appConfig->getAppRoot() . 'vendor/composer/autoload_classmap.php');
|
||||
|
||||
// if classmap has no entries return empty array
|
||||
if(count($classmap) == 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$classmapKeys = array_keys($classmap);
|
||||
return array_map(function($index) use ($classmapKeys){
|
||||
return new ClassmapEntry($classmapKeys[$index]);
|
||||
}, range(0, count($classmap) - 1));
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Classmap;
|
||||
|
||||
use HaydenPierce\ClassFinder\FinderInterface;
|
||||
|
||||
class ClassmapFinder implements FinderInterface
|
||||
{
|
||||
/** @var ClassmapEntryFactory */
|
||||
private $factory;
|
||||
|
||||
public function __construct(ClassmapEntryFactory $factory)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isNamespaceKnown($namespace)
|
||||
{
|
||||
$classmapEntries = $this->factory->getClassmapEntries();
|
||||
|
||||
foreach($classmapEntries as $classmapEntry) {
|
||||
if ($classmapEntry->knowsNamespace($namespace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
$classmapEntries = $this->factory->getClassmapEntries();
|
||||
|
||||
$matchingEntries = array_filter($classmapEntries, function(ClassmapEntry $entry) use ($namespace, $options) {
|
||||
return $entry->matches($namespace, $options);
|
||||
});
|
||||
|
||||
return array_map(function(ClassmapEntry $entry) {
|
||||
return $entry->getClassName();
|
||||
}, $matchingEntries);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Exception;
|
||||
|
||||
class ClassFinderException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Files;
|
||||
|
||||
class FilesEntry
|
||||
{
|
||||
/** @var string */
|
||||
private $file;
|
||||
|
||||
/** @var string */
|
||||
private $php;
|
||||
|
||||
/**
|
||||
* @param string $fileToInclude
|
||||
* @param string $php
|
||||
*/
|
||||
public function __construct($fileToInclude, $php)
|
||||
{
|
||||
$this->file = $this->normalizePath($fileToInclude);
|
||||
$this->php = $php;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function knowsNamespace($namespace)
|
||||
{
|
||||
$classes = $this->getClassesInFile();
|
||||
|
||||
foreach($classes as $class) {
|
||||
if (strpos($class, $namespace) !== false) {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of classes that belong to the given namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return string[]
|
||||
*/
|
||||
public function getClasses($namespace)
|
||||
{
|
||||
$classes = $this->getClassesInFile();
|
||||
|
||||
return array_values(array_filter($classes, function($class) use ($namespace) {
|
||||
$classNameFragments = explode('\\', $class);
|
||||
array_pop($classNameFragments);
|
||||
$classNamespace = implode('\\', $classNameFragments);
|
||||
|
||||
$namespace = trim($namespace, '\\');
|
||||
|
||||
return $namespace === $classNamespace;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically execute files and check for defined classes.
|
||||
*
|
||||
* This is where the real magic happens. Since classes in a randomly included file could contain classes in any namespace,
|
||||
* (or even multiple namespaces!) we must execute the file and check for newly defined classes. This has a potential
|
||||
* downside that files being executed will execute their side effects - which may be undesirable. However, Composer
|
||||
* will require these files anyway - so hopefully causing those side effects isn't that big of a deal.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getClassesInFile()
|
||||
{
|
||||
// get_declared_classes() returns a bunch of classes that are built into PHP. So we need a control here.
|
||||
$script = "var_export(get_declared_classes());";
|
||||
exec($this->php . " -r \"$script\"", $output);
|
||||
$classes = 'return ' . implode('', $output) . ';';
|
||||
$initialClasses = eval($classes);
|
||||
|
||||
// clear the exec() buffer.
|
||||
unset($output);
|
||||
|
||||
// This brings in the new classes. so $classes here will include the PHP defaults and the newly defined classes
|
||||
$script = "require_once '{$this->file}'; var_export(get_declared_classes());";
|
||||
exec($this->php . ' -r "' . $script . '"', $output);
|
||||
$classes = 'return ' . implode('', $output) . ';';
|
||||
$allClasses = eval($classes);
|
||||
|
||||
return array_diff($allClasses, $initialClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Similar to PSR4Namespace::normalizePath. Maybe we refactor?
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
private function normalizePath($path)
|
||||
{
|
||||
$path = str_replace('\\', '/', $path);
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Files;
|
||||
|
||||
use HaydenPierce\ClassFinder\AppConfig;
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class FilesEntryFactory
|
||||
{
|
||||
/** @var AppConfig */
|
||||
private $appConfig;
|
||||
|
||||
public function __construct(AppConfig $appConfig)
|
||||
{
|
||||
$this->appConfig = $appConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FilesEntry[]
|
||||
*/
|
||||
public function getFilesEntries()
|
||||
{
|
||||
$files = require($this->appConfig->getAppRoot() . 'vendor/composer/autoload_files.php');
|
||||
$phpPath = $this->findPHP();
|
||||
|
||||
$filesKeys = array_values($files);
|
||||
return array_map(function($index) use ($filesKeys, $phpPath){
|
||||
return new FilesEntry($filesKeys[$index], $phpPath);
|
||||
}, range(0, count($files) - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the PHP interrupter.
|
||||
*
|
||||
* If PHP 5.4 or newer is used, the PHP_BINARY value is used.
|
||||
* Otherwise we attempt to find it from shell commands.
|
||||
*
|
||||
* @return string
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
private function findPHP()
|
||||
{
|
||||
if (defined("PHP_BINARY")) {
|
||||
// PHP_BINARY was made available in PHP 5.4
|
||||
$php = PHP_BINARY;
|
||||
} else {
|
||||
$isHostWindows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
|
||||
if ($isHostWindows) {
|
||||
exec('where php', $output);
|
||||
$php = $output[0];
|
||||
} else {
|
||||
exec('which php', $output);
|
||||
$php = $output[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($php)) {
|
||||
throw new ClassFinderException(sprintf(
|
||||
'Could not locate PHP interrupter. See "%s" for details.',
|
||||
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/filesCouldNotLocatePHP.md'
|
||||
));
|
||||
}
|
||||
|
||||
return $php;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\Files;
|
||||
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
use HaydenPierce\ClassFinder\FinderInterface;
|
||||
|
||||
class FilesFinder implements FinderInterface
|
||||
{
|
||||
/** @var FilesEntryFactory */
|
||||
private $factory;
|
||||
|
||||
/**
|
||||
* @param FilesEntryFactory $factory
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function __construct(FilesEntryFactory $factory)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
|
||||
if (!function_exists('exec')) {
|
||||
throw new ClassFinderException(sprintf(
|
||||
'FilesFinder requires that exec() is available. Check your php.ini to see if it is disabled. See "%s" for details.',
|
||||
'https://gitlab.com/hpierce1102/ClassFinder/blob/master/docs/exceptions/filesExecNotAvailable.md'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isNamespaceKnown($namespace)
|
||||
{
|
||||
$filesEntries = $this->factory->getFilesEntries();
|
||||
|
||||
foreach($filesEntries as $filesEntry) {
|
||||
if ($filesEntry->knowsNamespace($namespace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
$filesEntries = $this->factory->getFilesEntries();
|
||||
|
||||
return array_reduce($filesEntries, function($carry, FilesEntry $entry) use ($namespace){
|
||||
return array_merge($carry, $entry->getClasses($namespace));
|
||||
}, array());
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder;
|
||||
|
||||
interface FinderInterface
|
||||
{
|
||||
/**
|
||||
* Find classes in a given namespace.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options);
|
||||
|
||||
/**
|
||||
* Check if a given namespace is known.
|
||||
*
|
||||
* A namespace is "known" if a Finder can determine that the autoloader can create classes from that namespace.
|
||||
*
|
||||
* For instance:
|
||||
* If given a classmap for "TestApp1\Foo\Bar\Baz", the namespace "TestApp1\Foo" is known, even if nothing loads
|
||||
* from that namespace directly. It is known because classes that include that namespace are known.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isNamespaceKnown($namespace);
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\PSR4;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use HaydenPierce\ClassFinder\FinderInterface;
|
||||
|
||||
class PSR4Finder implements FinderInterface
|
||||
{
|
||||
/** @var PSR4NamespaceFactory */
|
||||
private $factory;
|
||||
|
||||
public function __construct(PSR4NamespaceFactory $factory)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options)
|
||||
{
|
||||
if ($options === ClassFinder::RECURSIVE_MODE) {
|
||||
$applicableNamespaces = $this->findAllApplicableNamespaces($namespace);
|
||||
}
|
||||
|
||||
if (empty($applicableNamespaces)) {
|
||||
$bestNamespace = $this->findBestPSR4Namespace($namespace);
|
||||
$applicableNamespaces = array($bestNamespace);
|
||||
}
|
||||
|
||||
return array_reduce($applicableNamespaces, function($carry, $psr4NamespaceOrNull) use ($namespace, $options) {
|
||||
if ($psr4NamespaceOrNull instanceof PSR4Namespace) {
|
||||
$classes = $psr4NamespaceOrNull->findClasses($namespace, $options);
|
||||
} else {
|
||||
$classes = array();
|
||||
}
|
||||
|
||||
return array_merge($carry, $classes);
|
||||
}, array());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isNamespaceKnown($namespace)
|
||||
{
|
||||
$composerNamespaces = $this->factory->getPSR4Namespaces();
|
||||
|
||||
foreach($composerNamespaces as $psr4Namespace) {
|
||||
if ($psr4Namespace->knowsNamespace($namespace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return PSR4Namespace[]
|
||||
*/
|
||||
private function findAllApplicableNamespaces($namespace)
|
||||
{
|
||||
$composerNamespaces = $this->factory->getPSR4Namespaces();
|
||||
|
||||
return array_filter($composerNamespaces, function(PSR4Namespace $potentialNamespace) use ($namespace){
|
||||
return $potentialNamespace->isAcceptableNamespaceRecursiveMode($namespace);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return PSR4Namespace
|
||||
*/
|
||||
private function findBestPSR4Namespace($namespace)
|
||||
{
|
||||
$composerNamespaces = $this->factory->getPSR4Namespaces();
|
||||
|
||||
$acceptableNamespaces = array_filter($composerNamespaces, function(PSR4Namespace $potentialNamespace) use ($namespace){
|
||||
return $potentialNamespace->isAcceptableNamespace($namespace);
|
||||
});
|
||||
|
||||
$carry = new \stdClass();
|
||||
$carry->highestMatchingSegments = 0;
|
||||
$carry->bestNamespace = null;
|
||||
|
||||
/** @var PSR4Namespace $bestNamespace */
|
||||
$bestNamespace = array_reduce($acceptableNamespaces, function ($carry, PSR4Namespace $potentialNamespace) use ($namespace) {
|
||||
$matchingSegments = $potentialNamespace->countMatchingNamespaceSegments($namespace);
|
||||
|
||||
if ($matchingSegments > $carry->highestMatchingSegments) {
|
||||
$carry->highestMatchingSegments = $matchingSegments;
|
||||
$carry->bestNamespace = $potentialNamespace;
|
||||
}
|
||||
|
||||
return $carry;
|
||||
}, $carry);
|
||||
|
||||
return $bestNamespace->bestNamespace;
|
||||
}
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\PSR4;
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class PSR4Namespace
|
||||
{
|
||||
/** @var string */
|
||||
private $namespace;
|
||||
|
||||
/** @var string[] */
|
||||
private $directories;
|
||||
|
||||
/** @var PSR4Namespace[] */
|
||||
private $directSubnamespaces;
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param string[] $directories
|
||||
*/
|
||||
public function __construct($namespace, $directories)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->directories = $directories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function knowsNamespace($namespace)
|
||||
{
|
||||
$numberOfSegments = count(explode('\\', $namespace));
|
||||
$matchingSegments = $this->countMatchingNamespaceSegments($namespace);
|
||||
|
||||
if ($matchingSegments === 0) {
|
||||
// Provided namespace doesn't map to anything registered.
|
||||
return false;
|
||||
} elseif ($numberOfSegments <= $matchingSegments) {
|
||||
// This namespace is a superset of the provided namespace. Namespace is known.
|
||||
return true;
|
||||
} else {
|
||||
// This namespace is a subset of the provided namespace. We must resolve the remaining segments to a directory.
|
||||
$relativePath = substr($namespace, strlen($this->namespace));
|
||||
foreach ($this->directories as $directory) {
|
||||
$path = $this->normalizePath($directory, $relativePath);
|
||||
if (is_dir($path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines how many namespace segments match the internal namespace. This is useful because multiple namespaces
|
||||
* may technically match a registered namespace root, but one of the matches may be a better match. Namespaces that
|
||||
* match, but are not _the best_ match are incorrect matches. TestApp1\\ is **not** the best match when searching for
|
||||
* namespace TestApp1\\Multi\\Foo if TestApp1\\Multi was explicitly registered.
|
||||
*
|
||||
* PSR4Namespace $a;
|
||||
* $a->namespace = "TestApp1\\";
|
||||
* $a->countMatchingNamespaceSegments("TestApp1\\Multi") -> 1, TestApp1 matches.
|
||||
*
|
||||
* PSR4Namespace $b;
|
||||
* $b->namespace = "TestApp1\\Multi";
|
||||
* $b->countMatchingNamespaceSegments("TestApp1\\Multi") -> 2, TestApp1\\Multi matches
|
||||
*
|
||||
* PSR4Namespace $c;
|
||||
* $c->namespace = "HaydenPierce\\Foo\\Bar";
|
||||
* $c->countMatchingNamespaceSegments("TestApp1\\Multi") -> 0, No matches.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @return int
|
||||
*/
|
||||
public function countMatchingNamespaceSegments($namespace)
|
||||
{
|
||||
$namespaceFragments = explode('\\', $namespace);
|
||||
$undefinedNamespaceFragments = array();
|
||||
|
||||
while($namespaceFragments) {
|
||||
$possibleNamespace = implode('\\', $namespaceFragments) . '\\';
|
||||
|
||||
if(strpos($this->namespace, $possibleNamespace) !== false){
|
||||
return count(explode('\\', $possibleNamespace)) - 1;
|
||||
}
|
||||
|
||||
array_unshift($undefinedNamespaceFragments, array_pop($namespaceFragments));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isAcceptableNamespace($namespace)
|
||||
{
|
||||
$namespaceSegments = count(explode('\\', $this->namespace)) - 1;
|
||||
$matchingSegments = $this->countMatchingNamespaceSegments($namespace);
|
||||
return $namespaceSegments === $matchingSegments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return bool
|
||||
*/
|
||||
public function isAcceptableNamespaceRecursiveMode($namespace)
|
||||
{
|
||||
// Remove prefix backslash (TODO: review if we do this eariler).
|
||||
$namespace = ltrim($namespace, '\\');
|
||||
|
||||
return strpos($this->namespace, $namespace) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to identify subnamespaces.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function findDirectories()
|
||||
{
|
||||
$self = $this;
|
||||
$directories = array_reduce($this->directories, function($carry, $directory) use ($self){
|
||||
$path = $self->normalizePath($directory, '');
|
||||
$realDirectory = realpath($path);
|
||||
if ($realDirectory !== false) {
|
||||
return array_merge($carry, array($realDirectory));
|
||||
} else {
|
||||
return $carry;
|
||||
}
|
||||
}, array());
|
||||
|
||||
$arraysOfClasses = array_map(function($directory) use ($self) {
|
||||
$files = scandir($directory);
|
||||
return array_map(function($file) use ($directory, $self) {
|
||||
return $self->normalizePath($directory, $file);
|
||||
}, $files);
|
||||
}, $directories);
|
||||
|
||||
$potentialDirectories = array_reduce($arraysOfClasses, function($carry, $arrayOfClasses) {
|
||||
return array_merge($carry, $arrayOfClasses);
|
||||
}, array());
|
||||
|
||||
// Remove '.' and '..' directories
|
||||
$potentialDirectories = array_filter($potentialDirectories, function($potentialDirectory) {
|
||||
$segments = explode('/', $potentialDirectory);
|
||||
$lastSegment = array_pop($segments);
|
||||
|
||||
return $lastSegment !== '.' && $lastSegment !== '..';
|
||||
});
|
||||
|
||||
$confirmedDirectories = array_filter($potentialDirectories, function($potentialDirectory) {
|
||||
return is_dir($potentialDirectory);
|
||||
});
|
||||
|
||||
return $confirmedDirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param int $options
|
||||
* @return string[]
|
||||
*/
|
||||
public function findClasses($namespace, $options = ClassFinder::STANDARD_MODE)
|
||||
{
|
||||
$relativePath = substr($namespace, strlen($this->namespace));
|
||||
|
||||
$self = $this;
|
||||
$directories = array_reduce($this->directories, function($carry, $directory) use ($relativePath, $namespace, $self){
|
||||
$path = $self->normalizePath($directory, $relativePath);
|
||||
$realDirectory = realpath($path);
|
||||
if ($realDirectory !== false) {
|
||||
return array_merge($carry, array($realDirectory));
|
||||
} else {
|
||||
return $carry;
|
||||
}
|
||||
}, array());
|
||||
|
||||
$arraysOfClasses = array_map(function($directory) {
|
||||
return scandir($directory);
|
||||
}, $directories);
|
||||
|
||||
$potentialClassFiles = array_reduce($arraysOfClasses, function($carry, $arrayOfClasses) {
|
||||
return array_merge($carry, $arrayOfClasses);
|
||||
}, array());
|
||||
|
||||
$potentialClasses = array_map(function($file) use ($namespace){
|
||||
return $namespace . '\\' . str_replace('.php', '', $file);
|
||||
}, $potentialClassFiles);
|
||||
|
||||
if ($options == ClassFinder::RECURSIVE_MODE) {
|
||||
return $this->getClassesFromListRecursively($namespace);
|
||||
} else {
|
||||
return array_filter($potentialClasses, function($potentialClass) {
|
||||
if (function_exists($potentialClass)) {
|
||||
// For some reason calling class_exists() on a namespace'd function raises a Fatal Error (tested PHP 7.0.8)
|
||||
// Example: DeepCopy\deep_copy
|
||||
return false;
|
||||
} else {
|
||||
return class_exists($potentialClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getDirectClassesOnly()
|
||||
{
|
||||
$self = $this;
|
||||
$directories = array_reduce($this->directories, function($carry, $directory) use ($self){
|
||||
$path = $self->normalizePath($directory, '');
|
||||
$realDirectory = realpath($path);
|
||||
if ($realDirectory !== false) {
|
||||
return array_merge($carry, array($realDirectory));
|
||||
} else {
|
||||
return $carry;
|
||||
}
|
||||
}, array());
|
||||
|
||||
$arraysOfClasses = array_map(function($directory) {
|
||||
return scandir($directory);
|
||||
}, $directories);
|
||||
|
||||
$potentialClassFiles = array_reduce($arraysOfClasses, function($carry, $arrayOfClasses) {
|
||||
return array_merge($carry, $arrayOfClasses);
|
||||
}, array());
|
||||
|
||||
$selfNamespace = $this->namespace; // PHP 5.3 BC
|
||||
$potentialClasses = array_map(function($file) use ($self, $selfNamespace) {
|
||||
return $selfNamespace . str_replace('.php', '', $file);
|
||||
}, $potentialClassFiles);
|
||||
|
||||
return array_filter($potentialClasses, function($potentialClass) {
|
||||
if (function_exists($potentialClass)) {
|
||||
// For some reason calling class_exists() on a namespace'd function raises a Fatal Error (tested PHP 7.0.8)
|
||||
// Example: DeepCopy\deep_copy
|
||||
return false;
|
||||
} else {
|
||||
return class_exists($potentialClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @return string[]
|
||||
*/
|
||||
public function getClassesFromListRecursively($namespace)
|
||||
{
|
||||
$initialClasses = strpos( $this->namespace, $namespace) !== false ? $this->getDirectClassesOnly() : array();
|
||||
|
||||
return array_reduce($this->getDirectSubnamespaces(), function($carry, PSR4Namespace $subNamespace) use ($namespace) {
|
||||
return array_merge($carry, $subNamespace->getClassesFromListRecursively($namespace));
|
||||
}, $initialClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join an absolute path and a relative path in a platform agnostic way.
|
||||
*
|
||||
* This method is also extracted so that it can be turned into a vfs:// stream URL for unit testing.
|
||||
*
|
||||
* @param string $directory
|
||||
* @param string $relativePath
|
||||
* @return mixed
|
||||
*/
|
||||
public function normalizePath($directory, $relativePath)
|
||||
{
|
||||
$path = str_replace('\\', '/', $directory . '/' . $relativePath);
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PSR4Namespace[]
|
||||
*/
|
||||
public function getDirectSubnamespaces()
|
||||
{
|
||||
return $this->directSubnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PSR4Namespace[] $directSubnamespaces
|
||||
*/
|
||||
public function setDirectSubnamespaces($directSubnamespaces)
|
||||
{
|
||||
$this->directSubnamespaces = $directSubnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return trim($this->namespace, '\\');
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace HaydenPierce\ClassFinder\PSR4;
|
||||
|
||||
use HaydenPierce\ClassFinder\AppConfig;
|
||||
use HaydenPierce\ClassFinder\Exception\ClassFinderException;
|
||||
|
||||
class PSR4NamespaceFactory
|
||||
{
|
||||
/** @var AppConfig */
|
||||
private $appConfig;
|
||||
|
||||
public function __construct(AppConfig $appConfig)
|
||||
{
|
||||
$this->appConfig = $appConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPSR4Namespaces()
|
||||
{
|
||||
$namespaces = $this->getUserDefinedPSR4Namespaces();
|
||||
$vendorNamespaces = require($this->appConfig->getAppRoot() . 'vendor/composer/autoload_psr4.php');
|
||||
|
||||
$namespaces = array_merge($vendorNamespaces, $namespaces);
|
||||
|
||||
// There's some wackiness going on here for PHP 5.3 compatibility.
|
||||
$names = array_keys($namespaces);
|
||||
$directories = array_values($namespaces);
|
||||
$self = $this;
|
||||
$namespaces = array_map(function($index) use ($self, $names, $directories) {
|
||||
return $self->createNamespace($names[$index], $directories[$index]);
|
||||
},range(0, count($namespaces) - 1));
|
||||
|
||||
return $namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getUserDefinedPSR4Namespaces()
|
||||
{
|
||||
$appRoot = $this->appConfig->getAppRoot();
|
||||
|
||||
$composerJsonPath = $appRoot . 'composer.json';
|
||||
$composerConfig = json_decode(file_get_contents($composerJsonPath));
|
||||
|
||||
if (!isset($composerConfig->autoload)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
//Apparently PHP doesn't like hyphens, so we use variable variables instead.
|
||||
$psr4 = "psr-4";
|
||||
return (array)$composerConfig->autoload->$psr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a namespace from composer_psr4.php and composer.json autoload.psr4 items.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string[] $directories
|
||||
* @return PSR4Namespace
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function createNamespace($namespace, $directories)
|
||||
{
|
||||
if (is_string($directories)) {
|
||||
// This is an acceptable format according to composer.json
|
||||
$directories = array($directories);
|
||||
} elseif (is_array($directories)) {
|
||||
// composer_psr4.php seems to put everything in this format
|
||||
} else {
|
||||
throw new ClassFinderException('Unknown PSR4 definition.');
|
||||
}
|
||||
|
||||
$self = $this;
|
||||
$appConfig = $this->appConfig;
|
||||
$directories = array_map(function($directory) use ($self, $appConfig) {
|
||||
if ($self->isAbsolutePath($directory)) {
|
||||
return $directory;
|
||||
} else {
|
||||
return $appConfig->getAppRoot() . $directory;
|
||||
}
|
||||
}, $directories);
|
||||
|
||||
$directories = array_filter(array_map(function($directory) {
|
||||
return realpath($directory);
|
||||
}, $directories));
|
||||
|
||||
$psr4Namespace = new PSR4Namespace($namespace, $directories);
|
||||
|
||||
$subNamespaces = $this->getSubnamespaces($psr4Namespace);
|
||||
$psr4Namespace->setDirectSubnamespaces($subNamespaces);
|
||||
|
||||
return $psr4Namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PSR4Namespace $psr4Namespace
|
||||
* @return PSR4Namespace[]
|
||||
*/
|
||||
private function getSubnamespaces(PSR4Namespace $psr4Namespace)
|
||||
{
|
||||
// Scan it's own directories.
|
||||
$directories = $psr4Namespace->findDirectories();
|
||||
|
||||
$self = $this;
|
||||
$subnamespaces = array_map(function($directory) use ($self, $psr4Namespace){
|
||||
$segments = explode('/', $directory);
|
||||
$subnamespaceSegment = array_pop($segments);
|
||||
|
||||
$namespace = $psr4Namespace->getNamespace() . "\\" . $subnamespaceSegment . "\\";
|
||||
return $self->createNamespace($namespace, $directory);
|
||||
}, $directories);
|
||||
|
||||
return $subnamespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a path is absolute.
|
||||
*
|
||||
* Mostly this answer https://stackoverflow.com/a/38022806/3000068
|
||||
* A few changes: Changed exceptions to be ClassFinderExceptions, removed some ctype dependencies,
|
||||
* updated the root prefix regex to handle Window paths better.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
* @throws ClassFinderException
|
||||
*/
|
||||
public function isAbsolutePath($path) {
|
||||
if (!is_string($path)) {
|
||||
$mess = sprintf('String expected but was given %s', gettype($path));
|
||||
throw new ClassFinderException($mess);
|
||||
}
|
||||
|
||||
// Optional wrapper(s).
|
||||
$regExp = '%^(?<wrappers>(?:[[:print:]]{2,}://)*)';
|
||||
// Optional root prefix.
|
||||
$regExp .= '(?<root>(?:[[:alpha:]]:[/\\\\]|/)?)';
|
||||
// Actual path.
|
||||
$regExp .= '(?<path>(?:[[:print:]]*))$%';
|
||||
$parts = array();
|
||||
if (!preg_match($regExp, $path, $parts)) {
|
||||
$mess = sprintf('Path is NOT valid, was given %s', $path);
|
||||
throw new ClassFinderException($mess);
|
||||
}
|
||||
if ('' !== $parts['root']) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
preset: laravel
|
||||
risky: true
|
||||
enabled:
|
||||
- declare_strict_types
|
||||
- unalign_double_arrow
|
||||
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- Add `RequestParser` to convert an incoming HTTP request to one or more `OperationParams`
|
||||
Vendored
-16
@@ -1,16 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Benedikt Franke
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Vendored
-39
@@ -1,39 +0,0 @@
|
||||
# laragraph/utils
|
||||
|
||||
[](https://github.com/laragraph/utils/actions)
|
||||
[](https://codecov.io/gh/laragraph/utils)
|
||||
[](https://github.styleci.io/repos/228471198)
|
||||
|
||||
[](https://packagist.org/packages/laragraph/utils)
|
||||
[](https://packagist.org/packages/laragraph/utils)
|
||||
|
||||
Utilities for using GraphQL with Laravel
|
||||
|
||||
## Installation
|
||||
|
||||
Install through composer
|
||||
|
||||
```bash
|
||||
composer require laragraph/utils
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
This package holds basic utilities that are useful for building a GraphQL server with Laravel.
|
||||
If you want to build an application, we recommend using a full framework that integrates the
|
||||
primitives within this package:
|
||||
|
||||
- SDL-first: [Lighthouse](https://github.com/nuwave/lighthouse)
|
||||
- Code-first: [graphql-laravel](https://github.com/rebing/graphql-laravel)
|
||||
|
||||
## Changelog
|
||||
|
||||
See [`CHANGELOG.md`](CHANGELOG.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
See [`CONTRIBUTING.md`](.github/CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
This package is licensed using the MIT License.
|
||||
Vendored
-55
@@ -1,55 +0,0 @@
|
||||
{
|
||||
"name": "laragraph/utils",
|
||||
"type": "library",
|
||||
"description": "Utilities for using GraphQL with Laravel",
|
||||
"homepage": "https://github.com/laragraph/utils",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Benedikt Franke",
|
||||
"email": "benedikt@franke.tech"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"illuminate/contracts": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"illuminate/http": "5.6.* || 5.7.* || 5.8.* || ^6 || ^7 || ^8",
|
||||
"thecodingmachine/safe": "^1.1",
|
||||
"webonyx/graphql-php": "^0.13.2 || ^14"
|
||||
},
|
||||
"require-dev": {
|
||||
"ergebnis/composer-normalize": "^2.11",
|
||||
"infection/infection": "~0.20",
|
||||
"jangregor/phpstan-prophecy": "^0.8.1",
|
||||
"orchestra/testbench": "3.6.* || 3.7.* || 3.8.* || 3.9.* || ^4 || ^5 || ^6",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "^0.12.57",
|
||||
"phpstan/phpstan-deprecation-rules": "^0.12.5",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.5",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5",
|
||||
"thecodingmachine/phpstan-safe-rule": "^1.0"
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laragraph\\Utils\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Laragraph\\Utils\\Tests\\": "tests/"
|
||||
},
|
||||
"files": [
|
||||
"vendor/symfony/var-dumper/Resources/functions/dump.php"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"support": {
|
||||
"issues": "https://github.com/laragraph/utils/issues",
|
||||
"source": "https://github.com/laragraph/utils"
|
||||
}
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laragraph\Utils;
|
||||
|
||||
use GraphQL\Server\Helper;
|
||||
use GraphQL\Server\OperationParams;
|
||||
use GraphQL\Server\RequestError;
|
||||
use GraphQL\Utils\Utils;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class RequestParser
|
||||
{
|
||||
/**
|
||||
* @var \GraphQL\Server\Helper
|
||||
*/
|
||||
protected $helper;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->helper = new Helper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an incoming HTTP request to one or more OperationParams.
|
||||
*
|
||||
* @return \GraphQL\Server\OperationParams|array<int, \GraphQL\Server\OperationParams>
|
||||
*
|
||||
* @throws \GraphQL\Server\RequestError
|
||||
*/
|
||||
public function parseRequest(Request $request)
|
||||
{
|
||||
$method = $request->getMethod();
|
||||
$bodyParams = [];
|
||||
/** @var array<string, mixed> $queryParams */
|
||||
$queryParams = $request->query();
|
||||
|
||||
if ($method === 'POST') {
|
||||
/**
|
||||
* Never null, since Symfony defaults to application/x-www-form-urlencoded.
|
||||
*
|
||||
* @var string $contentType
|
||||
*/
|
||||
$contentType = $request->header('Content-Type');
|
||||
|
||||
if (stripos($contentType, 'application/json') !== false) {
|
||||
/** @var string $content */
|
||||
$content = $request->getContent();
|
||||
$bodyParams = \Safe\json_decode($content, true);
|
||||
|
||||
if (! is_array($bodyParams)) {
|
||||
throw new RequestError(
|
||||
'GraphQL Server expects JSON object or array, but got '.
|
||||
Utils::printSafeJson($bodyParams)
|
||||
);
|
||||
}
|
||||
} elseif (stripos($contentType, 'application/graphql') !== false) {
|
||||
/** @var string $content */
|
||||
$content = $request->getContent();
|
||||
$bodyParams = ['query' => $content];
|
||||
} elseif (stripos($contentType, 'application/x-www-form-urlencoded') !== false) {
|
||||
/** @var array<string, mixed> $bodyParams */
|
||||
$bodyParams = $request->post();
|
||||
} elseif (stripos($contentType, 'multipart/form-data') !== false) {
|
||||
$bodyParams = $this->inlineFiles($request);
|
||||
} else {
|
||||
throw new RequestError('Unexpected content type: '.Utils::printSafeJson($contentType));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->helper->parseRequestParams($method, $bodyParams, $queryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline file uploads given through a multipart request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array<mixed>
|
||||
*/
|
||||
protected function inlineFiles(Request $request): array
|
||||
{
|
||||
/** @var string|null $mapParam */
|
||||
$mapParam = $request->post('map');
|
||||
if ($mapParam === null) {
|
||||
throw new RequestError(
|
||||
'Could not find a valid map, be sure to conform to GraphQL multipart request specification: https://github.com/jaydenseric/graphql-multipart-request-spec'
|
||||
);
|
||||
}
|
||||
|
||||
/** @var string|null $operationsParam */
|
||||
$operationsParam = $request->post('operations');
|
||||
if ($operationsParam === null) {
|
||||
throw new RequestError(
|
||||
'Could not find valid operations, be sure to conform to GraphQL multipart request specification: https://github.com/jaydenseric/graphql-multipart-request-spec'
|
||||
);
|
||||
}
|
||||
|
||||
/** @var array<string, mixed>|array<int, array<string, mixed>> $operations */
|
||||
$operations = \Safe\json_decode($operationsParam, true);
|
||||
|
||||
/** @var array<int|string, array<int, string>> $map */
|
||||
$map = \Safe\json_decode($mapParam, true);
|
||||
|
||||
foreach ($map as $fileKey => $operationsPaths) {
|
||||
/** @var array<string> $operationsPaths */
|
||||
$file = $request->file((string) $fileKey);
|
||||
|
||||
/** @var string $operationsPath */
|
||||
foreach ($operationsPaths as $operationsPath) {
|
||||
Arr::set($operations, $operationsPath, $file);
|
||||
}
|
||||
}
|
||||
|
||||
return $operations;
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laragraph\Utils\Tests\Unit;
|
||||
|
||||
use GraphQL\Server\RequestError;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Laragraph\Utils\RequestParser;
|
||||
use Orchestra\Testbench\TestCase;
|
||||
use Safe\Exceptions\JsonException;
|
||||
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||
|
||||
class RequestParserTest extends TestCase
|
||||
{
|
||||
public function testGetWithQuery(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest('GET', ['query' => $query]);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testPostWithJson(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/json'],
|
||||
\Safe\json_encode(['query' => $query])
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testPostWithQueryApplicationGraphQL(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/graphql'],
|
||||
$query
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testPostWithRegularForm(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
['query' => $query],
|
||||
[],
|
||||
['Content-Type' => 'application/x-www-form-urlencoded']
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testPostDefaultsToRegularForm(): void
|
||||
{
|
||||
$query = /** @lang GraphQL */ '{ foo }';
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
['query' => $query]
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame($query, $params->query);
|
||||
}
|
||||
|
||||
public function testNonSensicalContentType(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'foobar']
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(RequestError::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
public function testNoQuery(): void
|
||||
{
|
||||
$request = $this->makeRequest('GET');
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame(null, $params->query);
|
||||
}
|
||||
|
||||
public function testInvalidJson(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/json'],
|
||||
'this is not valid json'
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(JsonException::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
public function testNonArrayJson(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
['Content-Type' => 'application/json'],
|
||||
'"this should be a map with query, variables, etc."'
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(RequestError::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
public function testMultipartFormRequest(): void
|
||||
{
|
||||
$file = UploadedFile::fake()->create('image.jpg', 500);
|
||||
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[
|
||||
'operations' => /** @lang JSON */ '
|
||||
{
|
||||
"query": "mutation Upload($file: Upload!) { upload(file: $file) }",
|
||||
"variables": {
|
||||
"file": null
|
||||
}
|
||||
}
|
||||
',
|
||||
'map' => /** @lang JSON */ '
|
||||
{
|
||||
"0": ["variables.file"]
|
||||
}
|
||||
',
|
||||
],
|
||||
[
|
||||
'0' => $file,
|
||||
],
|
||||
[
|
||||
'Content-Type' => 'multipart/form-data',
|
||||
]
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
/** @var \GraphQL\Server\OperationParams $params */
|
||||
$params = $parser->parseRequest($request);
|
||||
|
||||
self::assertSame('mutation Upload($file: Upload!) { upload(file: $file) }', $params->query);
|
||||
|
||||
$variables = $params->variables;
|
||||
self::assertNotNull($variables);
|
||||
/** @var array<string, mixed> $variables */
|
||||
self::assertSame($file, $variables['file']);
|
||||
}
|
||||
|
||||
public function testMultipartFormWithoutMap(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[],
|
||||
[],
|
||||
[
|
||||
'Content-Type' => 'multipart/form-data',
|
||||
]
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(RequestError::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
public function testMultipartFormWithoutOperations(): void
|
||||
{
|
||||
$request = $this->makeRequest(
|
||||
'POST',
|
||||
[
|
||||
'map' => /** @lang JSON */ '
|
||||
{
|
||||
"0": ["variables.file"]
|
||||
}
|
||||
',
|
||||
],
|
||||
[],
|
||||
[
|
||||
'Content-Type' => 'multipart/form-data',
|
||||
]
|
||||
);
|
||||
|
||||
$parser = new RequestParser();
|
||||
$this->expectException(RequestError::class);
|
||||
$parser->parseRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param array<mixed> $parameters
|
||||
* @param array<mixed> $files
|
||||
* @param array<mixed> $headers
|
||||
* @param string|resource|null $content
|
||||
* @return \Illuminate\Http\Request
|
||||
*/
|
||||
public function makeRequest(string $method, array $parameters = [], array $files = [], array $headers = [], $content = null): Request
|
||||
{
|
||||
$symfonyRequest = SymfonyRequest::create(
|
||||
'http://foo.bar/graphql',
|
||||
$method,
|
||||
$parameters,
|
||||
[],
|
||||
$files,
|
||||
$this->transformHeadersToServerVars($headers),
|
||||
$content
|
||||
);
|
||||
|
||||
return Request::createFromBase($symfonyRequest);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
composer.lock
|
||||
composer.phar
|
||||
/vendor/
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2016 container-interop
|
||||
Copyright (c) 2016 PHP Framework Interoperability Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
Container interface
|
||||
==============
|
||||
|
||||
This repository holds all interfaces related to [PSR-11 (Container Interface)][psr-url].
|
||||
|
||||
Note that this is not a Container implementation of its own. It is merely abstractions that describe the components of a Dependency Injection Container.
|
||||
|
||||
The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.
|
||||
|
||||
[psr-url]: https://www.php-fig.org/psr/psr-11/
|
||||
[package-url]: https://packagist.org/packages/psr/container
|
||||
[implementation-url]: https://packagist.org/providers/psr/container-implementation
|
||||
|
||||
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "psr/container",
|
||||
"type": "library",
|
||||
"description": "Common Container Interface (PHP FIG PSR-11)",
|
||||
"keywords": ["psr", "psr-11", "container", "container-interop", "container-interface"],
|
||||
"homepage": "https://github.com/php-fig/container",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Container\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Container;
|
||||
|
||||
/**
|
||||
* Base interface representing a generic exception in a container.
|
||||
*/
|
||||
interface ContainerExceptionInterface
|
||||
{
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psr\Container;
|
||||
|
||||
/**
|
||||
* Describes the interface of a container that exposes methods to read its entries.
|
||||
*/
|
||||
interface ContainerInterface
|
||||
{
|
||||
/**
|
||||
* Finds an entry of the container by its identifier and returns it.
|
||||
*
|
||||
* @param string $id Identifier of the entry to look for.
|
||||
*
|
||||
* @throws NotFoundExceptionInterface No entry was found for **this** identifier.
|
||||
* @throws ContainerExceptionInterface Error while retrieving the entry.
|
||||
*
|
||||
* @return mixed Entry.
|
||||
*/
|
||||
public function get(string $id);
|
||||
|
||||
/**
|
||||
* Returns true if the container can return an entry for the given identifier.
|
||||
* Returns false otherwise.
|
||||
*
|
||||
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
|
||||
* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
|
||||
*
|
||||
* @param string $id Identifier of the entry to look for.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $id);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Container;
|
||||
|
||||
/**
|
||||
* No entry was found in the container.
|
||||
*/
|
||||
interface NotFoundExceptionInterface extends ContainerExceptionInterface
|
||||
{
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
; This file is for unifying the coding style for different editors and IDEs.
|
||||
; More information at http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
@@ -1,2 +0,0 @@
|
||||
/vendor/
|
||||
composer.lock
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 PHP-FIG
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
PSR Event Dispatcher
|
||||
====================
|
||||
|
||||
This repository holds the interfaces related to [PSR-14](http://www.php-fig.org/psr/psr-14/).
|
||||
|
||||
Note that this is not an Event Dispatcher implementation of its own. It is merely interfaces that describe the components of an Event Dispatcher. See the specification for more details.
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "psr/event-dispatcher",
|
||||
"description": "Standard interfaces for event handling.",
|
||||
"type": "library",
|
||||
"keywords": ["psr", "psr-14", "events"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\EventDispatcher\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psr\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Defines a dispatcher for events.
|
||||
*/
|
||||
interface EventDispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Provide all relevant listeners with an event to process.
|
||||
*
|
||||
* @param object $event
|
||||
* The object to process.
|
||||
*
|
||||
* @return object
|
||||
* The Event that was passed, now modified by listeners.
|
||||
*/
|
||||
public function dispatch(object $event);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psr\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Mapper from an event to the listeners that are applicable to that event.
|
||||
*/
|
||||
interface ListenerProviderInterface
|
||||
{
|
||||
/**
|
||||
* @param object $event
|
||||
* An event for which to return the relevant listeners.
|
||||
* @return iterable[callable]
|
||||
* An iterable (array, iterator, or generator) of callables. Each
|
||||
* callable MUST be type-compatible with $event.
|
||||
*/
|
||||
public function getListenersForEvent(object $event) : iterable;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psr\EventDispatcher;
|
||||
|
||||
/**
|
||||
* An Event whose processing may be interrupted when the event has been handled.
|
||||
*
|
||||
* A Dispatcher implementation MUST check to determine if an Event
|
||||
* is marked as stopped after each listener is called. If it is then it should
|
||||
* return immediately without calling any further Listeners.
|
||||
*/
|
||||
interface StoppableEventInterface
|
||||
{
|
||||
/**
|
||||
* Is propagation stopped?
|
||||
*
|
||||
* This will typically only be used by the Dispatcher to determine if the
|
||||
* previous listener halted propagation.
|
||||
*
|
||||
* @return bool
|
||||
* True if the Event is complete and no further listeners should be called.
|
||||
* False to continue calling listeners.
|
||||
*/
|
||||
public function isPropagationStopped() : bool;
|
||||
}
|
||||
Vendored
-19
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2012 PHP Framework Interoperability Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
/**
|
||||
* This is a simple Logger implementation that other Loggers can inherit from.
|
||||
*
|
||||
* It simply delegates all log-level-specific methods to the `log` method to
|
||||
* reduce boilerplate code that a simple Logger that does the same thing with
|
||||
* messages regardless of the error level has to implement.
|
||||
*/
|
||||
abstract class AbstractLogger implements LoggerInterface
|
||||
{
|
||||
/**
|
||||
* System is unusable.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function emergency($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::EMERGENCY, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action must be taken immediately.
|
||||
*
|
||||
* Example: Entire website down, database unavailable, etc. This should
|
||||
* trigger the SMS alerts and wake you up.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function alert($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::ALERT, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical conditions.
|
||||
*
|
||||
* Example: Application component unavailable, unexpected exception.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function critical($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::CRITICAL, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime errors that do not require immediate action but should typically
|
||||
* be logged and monitored.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function error($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::ERROR, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptional occurrences that are not errors.
|
||||
*
|
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||
* that are not necessarily wrong.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function warning($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::WARNING, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal but significant events.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notice($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::NOTICE, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interesting events.
|
||||
*
|
||||
* Example: User logs in, SQL logs.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function info($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::INFO, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed debug information.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function debug($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::DEBUG, $message, $context);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
}
|
||||
Vendored
-18
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
/**
|
||||
* Describes log levels.
|
||||
*/
|
||||
class LogLevel
|
||||
{
|
||||
const EMERGENCY = 'emergency';
|
||||
const ALERT = 'alert';
|
||||
const CRITICAL = 'critical';
|
||||
const ERROR = 'error';
|
||||
const WARNING = 'warning';
|
||||
const NOTICE = 'notice';
|
||||
const INFO = 'info';
|
||||
const DEBUG = 'debug';
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
/**
|
||||
* Describes a logger-aware instance.
|
||||
*/
|
||||
interface LoggerAwareInterface
|
||||
{
|
||||
/**
|
||||
* Sets a logger instance on the object.
|
||||
*
|
||||
* @param LoggerInterface $logger
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger);
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
/**
|
||||
* Basic Implementation of LoggerAwareInterface.
|
||||
*/
|
||||
trait LoggerAwareTrait
|
||||
{
|
||||
/**
|
||||
* The logger instance.
|
||||
*
|
||||
* @var LoggerInterface|null
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Sets a logger.
|
||||
*
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
/**
|
||||
* Describes a logger instance.
|
||||
*
|
||||
* The message MUST be a string or object implementing __toString().
|
||||
*
|
||||
* The message MAY contain placeholders in the form: {foo} where foo
|
||||
* will be replaced by the context data in key "foo".
|
||||
*
|
||||
* The context array can contain arbitrary data. The only assumption that
|
||||
* can be made by implementors is that if an Exception instance is given
|
||||
* to produce a stack trace, it MUST be in a key named "exception".
|
||||
*
|
||||
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
|
||||
* for the full interface specification.
|
||||
*/
|
||||
interface LoggerInterface
|
||||
{
|
||||
/**
|
||||
* System is unusable.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function emergency($message, array $context = array());
|
||||
|
||||
/**
|
||||
* Action must be taken immediately.
|
||||
*
|
||||
* Example: Entire website down, database unavailable, etc. This should
|
||||
* trigger the SMS alerts and wake you up.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function alert($message, array $context = array());
|
||||
|
||||
/**
|
||||
* Critical conditions.
|
||||
*
|
||||
* Example: Application component unavailable, unexpected exception.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function critical($message, array $context = array());
|
||||
|
||||
/**
|
||||
* Runtime errors that do not require immediate action but should typically
|
||||
* be logged and monitored.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function error($message, array $context = array());
|
||||
|
||||
/**
|
||||
* Exceptional occurrences that are not errors.
|
||||
*
|
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||
* that are not necessarily wrong.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function warning($message, array $context = array());
|
||||
|
||||
/**
|
||||
* Normal but significant events.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notice($message, array $context = array());
|
||||
|
||||
/**
|
||||
* Interesting events.
|
||||
*
|
||||
* Example: User logs in, SQL logs.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function info($message, array $context = array());
|
||||
|
||||
/**
|
||||
* Detailed debug information.
|
||||
*
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function debug($message, array $context = array());
|
||||
|
||||
/**
|
||||
* Logs with an arbitrary level.
|
||||
*
|
||||
* @param mixed $level
|
||||
* @param string $message
|
||||
* @param mixed[] $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Psr\Log\InvalidArgumentException
|
||||
*/
|
||||
public function log($level, $message, array $context = array());
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
/**
|
||||
* This is a simple Logger trait that classes unable to extend AbstractLogger
|
||||
* (because they extend another class, etc) can include.
|
||||
*
|
||||
* It simply delegates all log-level-specific methods to the `log` method to
|
||||
* reduce boilerplate code that a simple Logger that does the same thing with
|
||||
* messages regardless of the error level has to implement.
|
||||
*/
|
||||
trait LoggerTrait
|
||||
{
|
||||
/**
|
||||
* System is unusable.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function emergency($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::EMERGENCY, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action must be taken immediately.
|
||||
*
|
||||
* Example: Entire website down, database unavailable, etc. This should
|
||||
* trigger the SMS alerts and wake you up.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function alert($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::ALERT, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical conditions.
|
||||
*
|
||||
* Example: Application component unavailable, unexpected exception.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function critical($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::CRITICAL, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime errors that do not require immediate action but should typically
|
||||
* be logged and monitored.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function error($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::ERROR, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptional occurrences that are not errors.
|
||||
*
|
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||
* that are not necessarily wrong.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function warning($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::WARNING, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal but significant events.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notice($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::NOTICE, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interesting events.
|
||||
*
|
||||
* Example: User logs in, SQL logs.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function info($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::INFO, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed debug information.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function debug($message, array $context = array())
|
||||
{
|
||||
$this->log(LogLevel::DEBUG, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs with an arbitrary level.
|
||||
*
|
||||
* @param mixed $level
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Psr\Log\InvalidArgumentException
|
||||
*/
|
||||
abstract public function log($level, $message, array $context = array());
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log;
|
||||
|
||||
/**
|
||||
* This Logger can be used to avoid conditional log calls.
|
||||
*
|
||||
* Logging should always be optional, and if no logger is provided to your
|
||||
* library creating a NullLogger instance to have something to throw logs at
|
||||
* is a good way to avoid littering your code with `if ($this->logger) { }`
|
||||
* blocks.
|
||||
*/
|
||||
class NullLogger extends AbstractLogger
|
||||
{
|
||||
/**
|
||||
* Logs with an arbitrary level.
|
||||
*
|
||||
* @param mixed $level
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Psr\Log\InvalidArgumentException
|
||||
*/
|
||||
public function log($level, $message, array $context = array())
|
||||
{
|
||||
// noop
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log\Test;
|
||||
|
||||
/**
|
||||
* This class is internal and does not follow the BC promise.
|
||||
*
|
||||
* Do NOT use this class in any way.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DummyTest
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return 'DummyTest';
|
||||
}
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log\Test;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Provides a base test class for ensuring compliance with the LoggerInterface.
|
||||
*
|
||||
* Implementors can extend the class and implement abstract methods to run this
|
||||
* as part of their test suite.
|
||||
*/
|
||||
abstract class LoggerInterfaceTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @return LoggerInterface
|
||||
*/
|
||||
abstract public function getLogger();
|
||||
|
||||
/**
|
||||
* This must return the log messages in order.
|
||||
*
|
||||
* The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>".
|
||||
*
|
||||
* Example ->error('Foo') would yield "error Foo".
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
abstract public function getLogs();
|
||||
|
||||
public function testImplements()
|
||||
{
|
||||
$this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideLevelsAndMessages
|
||||
*/
|
||||
public function testLogsAtAllLevels($level, $message)
|
||||
{
|
||||
$logger = $this->getLogger();
|
||||
$logger->{$level}($message, array('user' => 'Bob'));
|
||||
$logger->log($level, $message, array('user' => 'Bob'));
|
||||
|
||||
$expected = array(
|
||||
$level.' message of level '.$level.' with context: Bob',
|
||||
$level.' message of level '.$level.' with context: Bob',
|
||||
);
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function provideLevelsAndMessages()
|
||||
{
|
||||
return array(
|
||||
LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
|
||||
LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
|
||||
LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
|
||||
LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
|
||||
LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
|
||||
LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
|
||||
LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
|
||||
LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psr\Log\InvalidArgumentException
|
||||
*/
|
||||
public function testThrowsOnInvalidLevel()
|
||||
{
|
||||
$logger = $this->getLogger();
|
||||
$logger->log('invalid level', 'Foo');
|
||||
}
|
||||
|
||||
public function testContextReplacement()
|
||||
{
|
||||
$logger = $this->getLogger();
|
||||
$logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
|
||||
|
||||
$expected = array('info {Message {nothing} Bob Bar a}');
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function testObjectCastToString()
|
||||
{
|
||||
if (method_exists($this, 'createPartialMock')) {
|
||||
$dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString'));
|
||||
} else {
|
||||
$dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString'));
|
||||
}
|
||||
$dummy->expects($this->once())
|
||||
->method('__toString')
|
||||
->will($this->returnValue('DUMMY'));
|
||||
|
||||
$this->getLogger()->warning($dummy);
|
||||
|
||||
$expected = array('warning DUMMY');
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function testContextCanContainAnything()
|
||||
{
|
||||
$closed = fopen('php://memory', 'r');
|
||||
fclose($closed);
|
||||
|
||||
$context = array(
|
||||
'bool' => true,
|
||||
'null' => null,
|
||||
'string' => 'Foo',
|
||||
'int' => 0,
|
||||
'float' => 0.5,
|
||||
'nested' => array('with object' => new DummyTest),
|
||||
'object' => new \DateTime,
|
||||
'resource' => fopen('php://memory', 'r'),
|
||||
'closed' => $closed,
|
||||
);
|
||||
|
||||
$this->getLogger()->warning('Crazy context data', $context);
|
||||
|
||||
$expected = array('warning Crazy context data');
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
|
||||
public function testContextExceptionKeyCanBeExceptionOrOtherValues()
|
||||
{
|
||||
$logger = $this->getLogger();
|
||||
$logger->warning('Random message', array('exception' => 'oops'));
|
||||
$logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));
|
||||
|
||||
$expected = array(
|
||||
'warning Random message',
|
||||
'critical Uncaught Exception!'
|
||||
);
|
||||
$this->assertEquals($expected, $this->getLogs());
|
||||
}
|
||||
}
|
||||
-147
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\Log\Test;
|
||||
|
||||
use Psr\Log\AbstractLogger;
|
||||
|
||||
/**
|
||||
* Used for testing purposes.
|
||||
*
|
||||
* It records all records and gives you access to them for verification.
|
||||
*
|
||||
* @method bool hasEmergency($record)
|
||||
* @method bool hasAlert($record)
|
||||
* @method bool hasCritical($record)
|
||||
* @method bool hasError($record)
|
||||
* @method bool hasWarning($record)
|
||||
* @method bool hasNotice($record)
|
||||
* @method bool hasInfo($record)
|
||||
* @method bool hasDebug($record)
|
||||
*
|
||||
* @method bool hasEmergencyRecords()
|
||||
* @method bool hasAlertRecords()
|
||||
* @method bool hasCriticalRecords()
|
||||
* @method bool hasErrorRecords()
|
||||
* @method bool hasWarningRecords()
|
||||
* @method bool hasNoticeRecords()
|
||||
* @method bool hasInfoRecords()
|
||||
* @method bool hasDebugRecords()
|
||||
*
|
||||
* @method bool hasEmergencyThatContains($message)
|
||||
* @method bool hasAlertThatContains($message)
|
||||
* @method bool hasCriticalThatContains($message)
|
||||
* @method bool hasErrorThatContains($message)
|
||||
* @method bool hasWarningThatContains($message)
|
||||
* @method bool hasNoticeThatContains($message)
|
||||
* @method bool hasInfoThatContains($message)
|
||||
* @method bool hasDebugThatContains($message)
|
||||
*
|
||||
* @method bool hasEmergencyThatMatches($message)
|
||||
* @method bool hasAlertThatMatches($message)
|
||||
* @method bool hasCriticalThatMatches($message)
|
||||
* @method bool hasErrorThatMatches($message)
|
||||
* @method bool hasWarningThatMatches($message)
|
||||
* @method bool hasNoticeThatMatches($message)
|
||||
* @method bool hasInfoThatMatches($message)
|
||||
* @method bool hasDebugThatMatches($message)
|
||||
*
|
||||
* @method bool hasEmergencyThatPasses($message)
|
||||
* @method bool hasAlertThatPasses($message)
|
||||
* @method bool hasCriticalThatPasses($message)
|
||||
* @method bool hasErrorThatPasses($message)
|
||||
* @method bool hasWarningThatPasses($message)
|
||||
* @method bool hasNoticeThatPasses($message)
|
||||
* @method bool hasInfoThatPasses($message)
|
||||
* @method bool hasDebugThatPasses($message)
|
||||
*/
|
||||
class TestLogger extends AbstractLogger
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $records = [];
|
||||
|
||||
public $recordsByLevel = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function log($level, $message, array $context = [])
|
||||
{
|
||||
$record = [
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
'context' => $context,
|
||||
];
|
||||
|
||||
$this->recordsByLevel[$record['level']][] = $record;
|
||||
$this->records[] = $record;
|
||||
}
|
||||
|
||||
public function hasRecords($level)
|
||||
{
|
||||
return isset($this->recordsByLevel[$level]);
|
||||
}
|
||||
|
||||
public function hasRecord($record, $level)
|
||||
{
|
||||
if (is_string($record)) {
|
||||
$record = ['message' => $record];
|
||||
}
|
||||
return $this->hasRecordThatPasses(function ($rec) use ($record) {
|
||||
if ($rec['message'] !== $record['message']) {
|
||||
return false;
|
||||
}
|
||||
if (isset($record['context']) && $rec['context'] !== $record['context']) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, $level);
|
||||
}
|
||||
|
||||
public function hasRecordThatContains($message, $level)
|
||||
{
|
||||
return $this->hasRecordThatPasses(function ($rec) use ($message) {
|
||||
return strpos($rec['message'], $message) !== false;
|
||||
}, $level);
|
||||
}
|
||||
|
||||
public function hasRecordThatMatches($regex, $level)
|
||||
{
|
||||
return $this->hasRecordThatPasses(function ($rec) use ($regex) {
|
||||
return preg_match($regex, $rec['message']) > 0;
|
||||
}, $level);
|
||||
}
|
||||
|
||||
public function hasRecordThatPasses(callable $predicate, $level)
|
||||
{
|
||||
if (!isset($this->recordsByLevel[$level])) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->recordsByLevel[$level] as $i => $rec) {
|
||||
if (call_user_func($predicate, $rec, $i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
|
||||
$genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
|
||||
$level = strtolower($matches[2]);
|
||||
if (method_exists($this, $genericMethod)) {
|
||||
$args[] = $level;
|
||||
return call_user_func_array([$this, $genericMethod], $args);
|
||||
}
|
||||
}
|
||||
throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()');
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->records = [];
|
||||
$this->recordsByLevel = [];
|
||||
}
|
||||
}
|
||||
Vendored
-58
@@ -1,58 +0,0 @@
|
||||
PSR Log
|
||||
=======
|
||||
|
||||
This repository holds all interfaces/classes/traits related to
|
||||
[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).
|
||||
|
||||
Note that this is not a logger of its own. It is merely an interface that
|
||||
describes a logger. See the specification for more details.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```bash
|
||||
composer require psr/log
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
If you need a logger, you can use the interface like this:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Foo
|
||||
{
|
||||
private $logger;
|
||||
|
||||
public function __construct(LoggerInterface $logger = null)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function doSomething()
|
||||
{
|
||||
if ($this->logger) {
|
||||
$this->logger->info('Doing work');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->doSomethingElse();
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->error('Oh no!', array('exception' => $exception));
|
||||
}
|
||||
|
||||
// do something useful
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can then pick one of the implementations of the interface to get a logger.
|
||||
|
||||
If you want to implement the interface, you can require this package and
|
||||
implement `Psr\Log\LoggerInterface` in your code. Please read the
|
||||
[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
|
||||
for details.
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "psr/log",
|
||||
"description": "Common interface for logging libraries",
|
||||
"keywords": ["psr", "psr-3", "log"],
|
||||
"homepage": "https://github.com/php-fig/log",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "Psr/Log/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
; This file is for unifying the coding style for different editors and IDEs.
|
||||
; More information at http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 PHP Framework Interoperability Group
|
||||
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
> all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
> THE SOFTWARE.
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
PHP FIG Simple Cache PSR
|
||||
========================
|
||||
|
||||
This repository holds all interfaces related to PSR-16.
|
||||
|
||||
Note that this is not a cache implementation of its own. It is merely an interface that describes a cache implementation. See [the specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md) for more details.
|
||||
|
||||
You can find implementations of the specification by looking for packages providing the [psr/simple-cache-implementation](https://packagist.org/providers/psr/simple-cache-implementation) virtual package.
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "psr/simple-cache",
|
||||
"description": "Common interfaces for simple caching",
|
||||
"keywords": ["psr", "psr-16", "cache", "simple-cache", "caching"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\SimpleCache\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\SimpleCache;
|
||||
|
||||
/**
|
||||
* Interface used for all types of exceptions thrown by the implementing library.
|
||||
*/
|
||||
interface CacheException
|
||||
{
|
||||
}
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\SimpleCache;
|
||||
|
||||
interface CacheInterface
|
||||
{
|
||||
/**
|
||||
* Fetches a value from the cache.
|
||||
*
|
||||
* @param string $key The unique key of this item in the cache.
|
||||
* @param mixed $default Default value to return if the key does not exist.
|
||||
*
|
||||
* @return mixed The value of the item from the cache, or $default in case of cache miss.
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* MUST be thrown if the $key string is not a legal value.
|
||||
*/
|
||||
public function get($key, $default = null);
|
||||
|
||||
/**
|
||||
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
|
||||
*
|
||||
* @param string $key The key of the item to store.
|
||||
* @param mixed $value The value of the item to store, must be serializable.
|
||||
* @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and
|
||||
* the driver supports TTL then the library may set a default value
|
||||
* for it or let the driver take care of that.
|
||||
*
|
||||
* @return bool True on success and false on failure.
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* MUST be thrown if the $key string is not a legal value.
|
||||
*/
|
||||
public function set($key, $value, $ttl = null);
|
||||
|
||||
/**
|
||||
* Delete an item from the cache by its unique key.
|
||||
*
|
||||
* @param string $key The unique cache key of the item to delete.
|
||||
*
|
||||
* @return bool True if the item was successfully removed. False if there was an error.
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* MUST be thrown if the $key string is not a legal value.
|
||||
*/
|
||||
public function delete($key);
|
||||
|
||||
/**
|
||||
* Wipes clean the entire cache's keys.
|
||||
*
|
||||
* @return bool True on success and false on failure.
|
||||
*/
|
||||
public function clear();
|
||||
|
||||
/**
|
||||
* Obtains multiple cache items by their unique keys.
|
||||
*
|
||||
* @param iterable $keys A list of keys that can obtained in a single operation.
|
||||
* @param mixed $default Default value to return for keys that do not exist.
|
||||
*
|
||||
* @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* MUST be thrown if $keys is neither an array nor a Traversable,
|
||||
* or if any of the $keys are not a legal value.
|
||||
*/
|
||||
public function getMultiple($keys, $default = null);
|
||||
|
||||
/**
|
||||
* Persists a set of key => value pairs in the cache, with an optional TTL.
|
||||
*
|
||||
* @param iterable $values A list of key => value pairs for a multiple-set operation.
|
||||
* @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and
|
||||
* the driver supports TTL then the library may set a default value
|
||||
* for it or let the driver take care of that.
|
||||
*
|
||||
* @return bool True on success and false on failure.
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* MUST be thrown if $values is neither an array nor a Traversable,
|
||||
* or if any of the $values are not a legal value.
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null);
|
||||
|
||||
/**
|
||||
* Deletes multiple cache items in a single operation.
|
||||
*
|
||||
* @param iterable $keys A list of string-based keys to be deleted.
|
||||
*
|
||||
* @return bool True if the items were successfully removed. False if there was an error.
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* MUST be thrown if $keys is neither an array nor a Traversable,
|
||||
* or if any of the $keys are not a legal value.
|
||||
*/
|
||||
public function deleteMultiple($keys);
|
||||
|
||||
/**
|
||||
* Determines whether an item is present in the cache.
|
||||
*
|
||||
* NOTE: It is recommended that has() is only to be used for cache warming type purposes
|
||||
* and not to be used within your live applications operations for get/set, as this method
|
||||
* is subject to a race condition where your has() will return true and immediately after,
|
||||
* another script can remove it making the state of your app out of date.
|
||||
*
|
||||
* @param string $key The cache item key.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Psr\SimpleCache\InvalidArgumentException
|
||||
* MUST be thrown if the $key string is not a legal value.
|
||||
*/
|
||||
public function has($key);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psr\SimpleCache;
|
||||
|
||||
/**
|
||||
* Exception interface for invalid cache arguments.
|
||||
*
|
||||
* When an invalid argument is passed it must throw an exception which implements
|
||||
* this interface
|
||||
*/
|
||||
interface InvalidArgumentException extends CacheException
|
||||
{
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
# Changelog
|
||||
|
||||
## 7.0.1
|
||||
|
||||
* [FIXED] Infinite recursion in `presence_auth`.
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* [DEPRECATED] `get_channel_info`, `get_channels`, `socket_auth`, `presence_auth` in favour of camelCased versions
|
||||
* [DEPRECATED] `get_users_info` in favour of `getPresenceUsers`
|
||||
* [DEPRECATED] `ensure_valid_signature` in favour of `verifySignature`
|
||||
* [CHANGED] Restrict `$app_id` parameter of the `Pusher()` object to `string` (`int` was possible).
|
||||
* [ADDED] Return types.
|
||||
* [ADDED] Namespacing, PSR-12 formatting.
|
||||
|
||||
## 6.1.0
|
||||
|
||||
* [ADDED] triggerAsync and triggerBatchAsync using the Guzzle async interface.
|
||||
|
||||
## 6.0.1
|
||||
|
||||
* [CHANGED] Use type hints where possible (mixed type not available in PHP7).
|
||||
* [CHANGED] Document that functions can throw GuzzleException.
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* [CHANGED] internal HTTP client to Guzzle
|
||||
* [ADDED] optional client parameter to constructor
|
||||
* [CHANGED] useTLS is true by default
|
||||
* [REMOVED] `curl_options` from options
|
||||
* [REMOVED] customer logger
|
||||
* [REMOVED] host, port and timeout constructor parameters
|
||||
* [REMOVED] support for PHP 7.1
|
||||
* [CHANGED] lower severity level of logging to DEBUG level
|
||||
|
||||
## 5.0.3
|
||||
|
||||
* [CHANGED] Ensure version in Pusher.php is bumped on release.
|
||||
|
||||
## 5.0.2
|
||||
|
||||
* [CHANGED] Add release automation actions.
|
||||
|
||||
## 5.0.1
|
||||
|
||||
* [FIXED] Notice raised due to reference to potentially missing object property in `trigger` method
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* [CHANGED] The methods that make HTTP requests now throw an `ApiErrorException` instead of returning `false` for non-2xx responses
|
||||
* [CHANGED] `trigger` now accepts a `$params` associative array instead of a `$socket_id` as the third parameter
|
||||
* [ADDED] Support for requesting channel attributes as part of a `trigger` and `triggerBatch` request via an `info` parameter
|
||||
* [REMOVED] `debug` parameter from methods that make HTTP requests and from the constructor options
|
||||
* [REMOVED] Support for legacy push notifications (this has been superseded by https://github.com/pusher/push-notifications-php)
|
||||
|
||||
## 4.1.5
|
||||
|
||||
* [ADDED] Support for PHP 8.
|
||||
|
||||
## 4.1.4
|
||||
|
||||
* [FIXED] Errors in the failure path of `get_...` methods revealed by stricter type checking in PHP7.4
|
||||
|
||||
## 4.1.3
|
||||
|
||||
* No functional change, previous release was only partially successful
|
||||
|
||||
## 4.1.2
|
||||
|
||||
* [ADDED] option `encryption_master_key_base64`
|
||||
* [DEPRECATED] option `encryption_master_key`
|
||||
|
||||
## 4.1.1
|
||||
|
||||
* [ADDED] Support for PHP 7.4.
|
||||
|
||||
## 4.1.0
|
||||
|
||||
* [ADDED] `path` configuration option.
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* [REMOVED] Support for PHP 5.x, PHP 7.0 and HHVM.
|
||||
|
||||
## 3.4.1
|
||||
|
||||
* [ADDED] Support for PHP 7.3.
|
||||
|
||||
## 3.4.0
|
||||
|
||||
* [ADDED] `get_users_info` method.
|
||||
|
||||
## 3.3.1
|
||||
|
||||
* [FIXED] PHP Notice for Undefined `socket_id` in triggerBatch
|
||||
|
||||
## 3.3.0
|
||||
|
||||
* [ADDED] Support for End-to-end encrypted channels for triggerbatch
|
||||
* [FIXED] trigger behavior with mixtures of encrypted and non-encrypted channels
|
||||
|
||||
## 3.2.0
|
||||
|
||||
* [ADDED] This release adds support for end to end encrypted channels, a new feature for Channels. Read more [in our docs](https://pusher.com/docs/client_api_guide/client_encrypted_channels).
|
||||
* [DEPRECATED] Renamed `encrypted` option to `useTLS` - `encrypted` will still work!
|
||||
|
||||
## 3.1.0
|
||||
|
||||
* [ADDED] This release adds Webhook validation as well as a data structure to store Webhook payloads.
|
||||
|
||||
## 3.0.4
|
||||
|
||||
* [FIXED] Non zero indexed arrays of channels no longer get serialized as an object.
|
||||
|
||||
## 3.0.3
|
||||
|
||||
* [ADDED] PSR-3 logger compatibility.
|
||||
* [CHANGED] Improved PHP docs.
|
||||
|
||||
## 3.0.2
|
||||
|
||||
* [FIXED] Insufficient check for un-initialized curl resource.
|
||||
* [FIXED] Acceptance tests.
|
||||
|
||||
## 3.0.1
|
||||
|
||||
* [CHANGED] Info messages are now prefixed with INFO and errors are now prefixed with ERROR.
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* [NEW] Added namespaces (thanks [@vinkla](https://github.com/vinkla)).
|
||||
|
||||
## 2.6.4
|
||||
|
||||
* [FIXED] Log the curl error in more circumstances
|
||||
|
||||
## 2.6.1
|
||||
|
||||
* [FIXED] Check for correct status code when POSTing to native push notifications API.
|
||||
|
||||
## 2.6.0
|
||||
|
||||
* [ADDED] support for publishing push notifications on up to 10 interests.
|
||||
|
||||
## 2.5.0
|
||||
|
||||
* [REMOVED] Native push notifications payload validation in the client.
|
||||
|
||||
## 2.5.0-rc2
|
||||
|
||||
* [FIXED] DDN and Native Push endpoints were not assembled correctly.
|
||||
|
||||
## 2.5.0-rc1
|
||||
|
||||
* [NEW] Native push notifications
|
||||
|
||||
## 2.4.2
|
||||
|
||||
* [CHANGED] One curl instance per Pusher instance
|
||||
|
||||
## 2.4.1
|
||||
|
||||
* [FIXED] Presence data could not be submitted after the style changes
|
||||
|
||||
## 2.4.0
|
||||
|
||||
* [ADDED] Support for batch events
|
||||
* [ADDED] Curl options
|
||||
* [FIXED] Applied fixes from StyleCI
|
||||
|
||||
## 2.3.0
|
||||
|
||||
* [ADDED] A new `cluster` option for the Pusher constructor.
|
||||
|
||||
## 2.2.2
|
||||
|
||||
* [FIXED] Fixed a PHP 5.2 incompatibility caused by referencing a private method in array_walk.
|
||||
|
||||
## 2.2.1
|
||||
|
||||
* [FIXED] Channel name and socket_id values are now validated.
|
||||
* [BROKE] Inadvertently broke PHP 5.2 compatibility by referencing a private method in array_walk.
|
||||
|
||||
## 2.2.0
|
||||
|
||||
* [CHANGED] `new Pusher($app_key, $app_secret, $app_id, $options)` - The `$options` parameter
|
||||
has been added as the forth parameter to the constructor and other additional
|
||||
parameters are now deprecated.
|
||||
|
||||
## 2.1.3
|
||||
|
||||
* [NEW] `$pusher->trigger` can now take an `array` of channel names as a first parameter to allow the same event to be published on multiple channels.
|
||||
* [NEW] `$pusher->get` generic function can be used to make `GET` calls to the REST API
|
||||
* [NEW] `$pusher->set_logger` to allow internal logging to be exposed and logged in your own logs.
|
||||
|
||||
## 2.1.2
|
||||
|
||||
* [CHANGED] Debug response from `$pusher->trigger` call is now an associative array in the form `array( 'body' => '{String} body text of response', 'status' => '{Number} http status of the response' )`
|
||||
|
||||
## 2.1.1
|
||||
|
||||
* [CHANGED] Added optional $options parameter to get_channel_info. get_channel_info($channel, $options = array() )
|
||||
|
||||
## 2.1.0
|
||||
|
||||
* [CHANGED] Renamed get_channel_stats to get_channel_info
|
||||
* [CHANGED] get_channels now takes and $options parameter. get_channels( $options = array() )
|
||||
* [REMOVED] get_presence_channels
|
||||
|
||||
## 2.0.1
|
||||
|
||||
* [FIXED] Overwritten socket_id parameter in trigger: https://github.com/pusher/pusher-php-server/pull/3
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* [NEW] Versioning introduced at 2.0.0
|
||||
* [NEW] Added composer.json for submission to http://packagist.org/
|
||||
* [CHANGED] `get_channels()` now returns an object which has a `channels` property. This must be accessed to get the Array of channels in an application.
|
||||
* [CHANGED] `get_presence_channels()` now returns an object which has a `channels` property. This must be accessed to get the Array of channels in an application.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "pusher/pusher-php-server",
|
||||
"description" : "Library for interacting with the Pusher REST API",
|
||||
"keywords": ["php-pusher-server", "pusher", "rest", "realtime", "real-time", "real time", "messaging", "push", "trigger", "publish", "events"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"psr/log": "^1.0",
|
||||
"paragonie/sodium_compat": "^1.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5|^9.3",
|
||||
"overtrue/phplint": "^2.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pusher\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "": "tests/" }
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0-dev"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
## Description
|
||||
|
||||
Add a short description of the change. If this is related to an issue, please add a reference to the issue.
|
||||
|
||||
## CHANGELOG
|
||||
|
||||
* [CHANGED] Describe your change here. Look at CHANGELOG.md to see the format.
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Pusher;
|
||||
|
||||
/**
|
||||
* HTTP error responses.
|
||||
* getCode() will return the response HTTP status code,
|
||||
* and getMessage() will return the response body.
|
||||
*/
|
||||
class ApiErrorException extends PusherException
|
||||
{
|
||||
/**
|
||||
* Returns the string representation of the exception.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return "(Status {$this->getCode()}) {$this->getMessage()}";
|
||||
}
|
||||
}
|
||||
+915
@@ -0,0 +1,915 @@
|
||||
<?php
|
||||
|
||||
namespace Pusher;
|
||||
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
|
||||
class Pusher implements LoggerAwareInterface, PusherInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var string Version
|
||||
*/
|
||||
public static $VERSION = '7.0.1';
|
||||
|
||||
/**
|
||||
* @var null|PusherCrypto
|
||||
*/
|
||||
private $crypto;
|
||||
|
||||
/**
|
||||
* @var array Settings
|
||||
*/
|
||||
private $settings = [
|
||||
'scheme' => 'http',
|
||||
'port' => 80,
|
||||
'path' => '',
|
||||
'timeout' => 30,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var null|resource
|
||||
*/
|
||||
private $client = null; // Guzzle client
|
||||
|
||||
/**
|
||||
* Initializes a new Pusher instance with key, secret, app ID and channel.
|
||||
*
|
||||
* @param string $auth_key
|
||||
* @param string $secret
|
||||
* @param string $app_id
|
||||
* @param array $options [optional]
|
||||
* Options to configure the Pusher instance.
|
||||
* scheme - e.g. http or https
|
||||
* host - the host e.g. api-mt1.pusher.com. No trailing forward slash.
|
||||
* port - the http port
|
||||
* timeout - the http timeout
|
||||
* useTLS - quick option to use scheme of https and port 443 (default is true).
|
||||
* cluster - cluster name to connect to.
|
||||
* encryption_master_key_base64 - a 32 byte key, encoded as base64. This key, along with the channel name, are used to derive per-channel encryption keys. Per-channel keys are used to encrypt event data on encrypted channels.
|
||||
* @param ClientInterface|null $client [optional] - a Guzzle client to use for all HTTP requests
|
||||
*
|
||||
* @throws PusherException Throws exception if any required dependencies are missing
|
||||
*/
|
||||
public function __construct(string $auth_key, string $secret, string $app_id, array $options = [], ClientInterface $client = null)
|
||||
{
|
||||
$this->check_compatibility();
|
||||
|
||||
if (!is_null($client)) {
|
||||
$this->client = $client;
|
||||
} else {
|
||||
$this->client = new \GuzzleHttp\Client();
|
||||
}
|
||||
|
||||
$useTLS = true;
|
||||
if (isset($options['useTLS'])) {
|
||||
$useTLS = $options['useTLS'] === true;
|
||||
}
|
||||
if (
|
||||
$useTLS &&
|
||||
!isset($options['scheme']) &&
|
||||
!isset($options['port'])
|
||||
) {
|
||||
$options['scheme'] = 'https';
|
||||
$options['port'] = 443;
|
||||
}
|
||||
|
||||
$this->settings['auth_key'] = $auth_key;
|
||||
$this->settings['secret'] = $secret;
|
||||
$this->settings['app_id'] = $app_id;
|
||||
$this->settings['base_path'] = '/apps/' . $this->settings['app_id'];
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
// only set if valid setting/option
|
||||
if (isset($this->settings[$key])) {
|
||||
$this->settings[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// handle the case when 'host' and 'cluster' are specified in the options.
|
||||
if (!array_key_exists('host', $this->settings)) {
|
||||
if (array_key_exists('host', $options)) {
|
||||
$this->settings['host'] = $options['host'];
|
||||
} elseif (array_key_exists('cluster', $options)) {
|
||||
$this->settings['host'] = 'api-' . $options['cluster'] . '.pusher.com';
|
||||
} else {
|
||||
$this->settings['host'] = 'api-mt1.pusher.com';
|
||||
}
|
||||
}
|
||||
|
||||
// ensure host doesn't have a scheme prefix
|
||||
$this->settings['host'] = preg_replace('/http[s]?\:\/\//', '', $this->settings['host'], 1);
|
||||
|
||||
if (!array_key_exists('encryption_master_key_base64', $options)) {
|
||||
$options['encryption_master_key_base64'] = '';
|
||||
}
|
||||
|
||||
if ($options['encryption_master_key_base64'] !== '') {
|
||||
$parsedKey = PusherCrypto::parse_master_key(
|
||||
$options['encryption_master_key_base64']
|
||||
);
|
||||
$this->crypto = new PusherCrypto($parsedKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSettings(): array
|
||||
{
|
||||
return $this->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a string.
|
||||
*
|
||||
* @param string $msg The message to log
|
||||
* @param array|\Exception $context [optional] Any extraneous information that does not fit well in a string.
|
||||
* @param string $level [optional] Importance of log message, highly recommended to use Psr\Log\LogLevel::{level}
|
||||
*/
|
||||
private function log(string $msg, array $context = [], string $level = LogLevel::DEBUG): void
|
||||
{
|
||||
if (is_null($this->logger)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->logger instanceof LoggerInterface) {
|
||||
$this->logger->log($level, $msg, $context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Support old style logger (deprecated)
|
||||
$msg = sprintf('Pusher: %s: %s', strtoupper($level), $msg);
|
||||
$replacement = [];
|
||||
|
||||
foreach ($context as $k => $v) {
|
||||
$replacement['{' . $k . '}'] = $v;
|
||||
}
|
||||
|
||||
$this->logger->log($level, strtr($msg, $replacement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current PHP setup is sufficient to run this class.
|
||||
*
|
||||
* @throws PusherException If any required dependencies are missing
|
||||
*/
|
||||
private function check_compatibility(): void
|
||||
{
|
||||
if (!extension_loaded('json')) {
|
||||
throw new PusherException('The Pusher library requires the PHP JSON module. Please ensure it is installed');
|
||||
}
|
||||
|
||||
if (!in_array('sha256', hash_algos(), true)) {
|
||||
throw new PusherException('SHA256 appears to be unsupported - make sure you have support for it, or upgrade your version of PHP.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate number of channels and channel name format.
|
||||
*
|
||||
* @param string[] $channels An array of channel names to validate
|
||||
*
|
||||
* @throws PusherException If $channels is too big or any channel is invalid
|
||||
*/
|
||||
private function validate_channels(array $channels): void
|
||||
{
|
||||
if (count($channels) > 100) {
|
||||
throw new PusherException('An event can be triggered on a maximum of 100 channels in a single call.');
|
||||
}
|
||||
|
||||
foreach ($channels as $channel) {
|
||||
$this->validate_channel($channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a channel name is valid based on our spec.
|
||||
*
|
||||
* @param string $channel The channel name to validate
|
||||
*
|
||||
* @throws PusherException If $channel is invalid
|
||||
*/
|
||||
private function validate_channel(string $channel): void
|
||||
{
|
||||
if (!preg_match('/\A[-a-zA-Z0-9_=@,.;]+\z/', $channel)) {
|
||||
throw new PusherException('Invalid channel name ' . $channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a socket_id is valid based on our spec.
|
||||
*
|
||||
* @param string $socket_id The socket ID to validate
|
||||
*
|
||||
* @throws PusherException If $socket_id is invalid
|
||||
*/
|
||||
private function validate_socket_id(string $socket_id): void
|
||||
{
|
||||
if ($socket_id !== null && !preg_match('/\A\d+\.\d+\z/', $socket_id)) {
|
||||
throw new PusherException('Invalid socket ID ' . $socket_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function used to generate signing headers
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $request_method
|
||||
* @param array $query_params [optional]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function sign(string $path, string $request_method = 'GET', array $query_params = []): array
|
||||
{
|
||||
return self::build_auth_query_params(
|
||||
$this->settings['auth_key'],
|
||||
$this->settings['secret'],
|
||||
$request_method,
|
||||
$path,
|
||||
$query_params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Channels url prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function channels_url_prefix(): string
|
||||
{
|
||||
return $this->settings['scheme'] . '://' . $this->settings['host'] . ':' . $this->settings['port'] . $this->settings['path'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the required HMAC'd auth string.
|
||||
*
|
||||
* @param string $auth_key
|
||||
* @param string $auth_secret
|
||||
* @param string $request_method
|
||||
* @param string $request_path
|
||||
* @param array $query_params [optional]
|
||||
* @param string $auth_version [optional]
|
||||
* @param string|null $auth_timestamp [optional]
|
||||
* @return array
|
||||
*/
|
||||
public static function build_auth_query_params(
|
||||
string $auth_key,
|
||||
string $auth_secret,
|
||||
string $request_method,
|
||||
string $request_path,
|
||||
array $query_params = [],
|
||||
string $auth_version = '1.0',
|
||||
string $auth_timestamp = null
|
||||
): array {
|
||||
$params = [];
|
||||
$params['auth_key'] = $auth_key;
|
||||
$params['auth_timestamp'] = (is_null($auth_timestamp) ? time() : $auth_timestamp);
|
||||
$params['auth_version'] = $auth_version;
|
||||
|
||||
$params = array_merge($params, $query_params);
|
||||
ksort($params);
|
||||
|
||||
$string_to_sign = "$request_method\n" . $request_path . "\n" . self::array_implode('=', '&', $params);
|
||||
|
||||
$auth_signature = hash_hmac('sha256', $string_to_sign, $auth_secret, false);
|
||||
|
||||
$params['auth_signature'] = $auth_signature;
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implode an array with the key and value pair giving
|
||||
* a glue, a separator between pairs and the array
|
||||
* to implode.
|
||||
*
|
||||
* @param string $glue The glue between key and value
|
||||
* @param string $separator Separator between pairs
|
||||
* @param array|string $array The array to implode
|
||||
*
|
||||
* @return string The imploded array
|
||||
*/
|
||||
public static function array_implode(string $glue, string $separator, $array): string
|
||||
{
|
||||
if (!is_array($array)) {
|
||||
return $array;
|
||||
}
|
||||
|
||||
$string = [];
|
||||
foreach ($array as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
$val = implode(',', $val);
|
||||
}
|
||||
$string[] = "{$key}{$glue}{$val}";
|
||||
}
|
||||
|
||||
return implode($separator, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prepare trigger request. Takes the same
|
||||
* parameters as the public trigger functions.
|
||||
*
|
||||
* @param array|string $channels A channel name or an array of channel names to publish the event on.
|
||||
* @param string $event
|
||||
* @param mixed $data Event data
|
||||
* @param array $params [optional]
|
||||
* @param bool $already_encoded [optional]
|
||||
*
|
||||
* @return Request
|
||||
* @throws PusherException Throws PusherException if $channels is an array of size 101 or above or $socket_id is invalid
|
||||
*/
|
||||
public function make_request($channels, string $event, $data, array $params = [], bool $already_encoded = false): Request
|
||||
{
|
||||
if (is_string($channels) === true) {
|
||||
$channels = [$channels];
|
||||
}
|
||||
|
||||
$this->validate_channels($channels);
|
||||
if (isset($params['socket_id'])) {
|
||||
$this->validate_socket_id($params['socket_id']);
|
||||
}
|
||||
|
||||
$has_encrypted_channel = false;
|
||||
foreach ($channels as $chan) {
|
||||
if (PusherCrypto::is_encrypted_channel($chan)) {
|
||||
$has_encrypted_channel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($has_encrypted_channel) {
|
||||
if (count($channels) > 1) {
|
||||
// For rationale, see limitations of end-to-end encryption in the README
|
||||
throw new PusherException('You cannot trigger to multiple channels when using encrypted channels');
|
||||
} else {
|
||||
try {
|
||||
$data_encoded = $this->crypto->encrypt_payload(
|
||||
$channels[0],
|
||||
$already_encoded ? $data : json_encode($data, JSON_THROW_ON_ERROR)
|
||||
);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$data_encoded = $already_encoded ? $data : json_encode($data, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
}
|
||||
|
||||
$query_params = [];
|
||||
|
||||
$path = $this->settings['base_path'] . '/events';
|
||||
|
||||
// json_encode might return false on failure
|
||||
if (!$data_encoded) {
|
||||
$this->log('Failed to perform json_encode on the the provided data: {error}', [
|
||||
'error' => print_r($data, true),
|
||||
], LogLevel::ERROR);
|
||||
}
|
||||
|
||||
$post_params = [];
|
||||
$post_params['name'] = $event;
|
||||
$post_params['data'] = $data_encoded;
|
||||
$post_params['channels'] = array_values($channels);
|
||||
|
||||
$all_params = array_merge($post_params, $params);
|
||||
|
||||
try {
|
||||
$post_value = json_encode($all_params, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
|
||||
$query_params['body_md5'] = md5($post_value);
|
||||
|
||||
$signature = $this->sign($path, 'POST', $query_params);
|
||||
|
||||
$this->log('trigger POST: {post_value}', compact('post_value'));
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Pusher-Library' => 'pusher-http-php ' . self::$VERSION
|
||||
];
|
||||
|
||||
$params = array_merge($signature, $query_params);
|
||||
$query_string = self::array_implode('=', '&', $params);
|
||||
$full_path = $path . "?" . $query_string;
|
||||
return new Request('POST', $full_path, $headers, $post_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger an event by providing event name and payload.
|
||||
* Optionally provide a socket ID to exclude a client (most likely the sender).
|
||||
*
|
||||
* @param array|string $channels A channel name or an array of channel names to publish the event on.
|
||||
* @param string $event
|
||||
* @param mixed $data Event data
|
||||
* @param array $params [optional]
|
||||
* @param bool $already_encoded [optional]
|
||||
*
|
||||
* @return object
|
||||
* @throws ApiErrorException Throws ApiErrorException if the Channels HTTP API responds with an error
|
||||
* @throws GuzzleException
|
||||
* @throws PusherException Throws PusherException if $channels is an array of size 101 or above or $socket_id is invalid
|
||||
*/
|
||||
public function trigger($channels, string $event, $data, array $params = [], bool $already_encoded = false): object
|
||||
{
|
||||
$request = $this->make_request($channels, $event, $data, $params, $already_encoded);
|
||||
|
||||
$response = $this->client->send($request, [
|
||||
'http_errors' => false,
|
||||
'base_uri' => $this->channels_url_prefix()
|
||||
]);
|
||||
|
||||
$status = $response->getStatusCode();
|
||||
|
||||
if ($status !== 200) {
|
||||
$body = (string) $response->getBody();
|
||||
throw new ApiErrorException($body, $status);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
|
||||
if (property_exists($result, 'channels')) {
|
||||
$result->channels = get_object_vars($result->channels);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously trigger an event by providing event name and payload.
|
||||
* Optionally provide a socket ID to exclude a client (most likely the sender).
|
||||
*
|
||||
* @param array|string $channels A channel name or an array of channel names to publish the event on.
|
||||
* @param string $event
|
||||
* @param mixed $data Event data
|
||||
* @param array $params [optional]
|
||||
* @param bool $already_encoded [optional]
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @throws PusherException
|
||||
*/
|
||||
public function triggerAsync($channels, string $event, $data, array $params = [], bool $already_encoded = false): PromiseInterface
|
||||
{
|
||||
$request = $this->make_request($channels, $event, $data, $params, $already_encoded);
|
||||
|
||||
$promise = $this->client->sendAsync($request, [
|
||||
'http_errors' => false,
|
||||
'base_uri' => $this->channels_url_prefix()
|
||||
])->then(function ($response) {
|
||||
$status = $response->getStatusCode();
|
||||
|
||||
if ($status !== 200) {
|
||||
$body = (string) $response->getBody();
|
||||
throw new ApiErrorException($body, $status);
|
||||
}
|
||||
|
||||
$result = json_decode($response->getBody(), null, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
if (property_exists($result, 'channels')) {
|
||||
$result->channels = get_object_vars($result->channels);
|
||||
}
|
||||
|
||||
return $result;
|
||||
});
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prepare batch trigger request. Takes the same * parameters as the public batch trigger functions.
|
||||
*
|
||||
* @param array $batch [optional] An array of events to send
|
||||
* @param bool $already_encoded [optional]
|
||||
*
|
||||
* @return Request
|
||||
* @throws PusherException
|
||||
*/
|
||||
public function make_batch_request(array $batch = [], bool $already_encoded = false): Request
|
||||
{
|
||||
foreach ($batch as $key => $event) {
|
||||
$this->validate_channel($event['channel']);
|
||||
if (isset($event['socket_id'])) {
|
||||
$this->validate_socket_id($event['socket_id']);
|
||||
}
|
||||
|
||||
$data = $event['data'];
|
||||
if (!is_string($data)) {
|
||||
try {
|
||||
$data = $already_encoded ? $data : json_encode($data, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
}
|
||||
|
||||
if (PusherCrypto::is_encrypted_channel($event['channel'])) {
|
||||
$batch[$key]['data'] = $this->crypto->encrypt_payload($event['channel'], $data);
|
||||
} else {
|
||||
$batch[$key]['data'] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
$post_params = [];
|
||||
$post_params['batch'] = $batch;
|
||||
try {
|
||||
$post_value = json_encode($post_params, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
|
||||
$query_params = [];
|
||||
$query_params['body_md5'] = md5($post_value);
|
||||
$path = $this->settings['base_path'] . '/batch_events';
|
||||
|
||||
$signature = $this->sign($path, 'POST', $query_params);
|
||||
|
||||
$this->log('trigger POST: {post_value}', compact('post_value'));
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Pusher-Library' => 'pusher-http-php ' . self::$VERSION
|
||||
];
|
||||
|
||||
$params = array_merge($signature, $query_params);
|
||||
$query_string = self::array_implode('=', '&', $params);
|
||||
$full_path = $path . "?" . $query_string;
|
||||
return new Request('POST', $full_path, $headers, $post_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger multiple events at the same time.
|
||||
*
|
||||
* @param array $batch [optional] An array of events to send
|
||||
* @param bool $already_encoded [optional]
|
||||
*
|
||||
* @return object
|
||||
* @throws ApiErrorException Throws ApiErrorException if the Channels HTTP API responds with an error
|
||||
* @throws GuzzleException
|
||||
* @throws PusherException
|
||||
*/
|
||||
public function triggerBatch(array $batch = [], bool $already_encoded = false): object
|
||||
{
|
||||
$request = $this->make_batch_request($batch, $already_encoded);
|
||||
|
||||
$response = $this->client->send($request, [
|
||||
'http_errors' => false,
|
||||
'base_uri' => $this->channels_url_prefix()
|
||||
]);
|
||||
|
||||
$status = $response->getStatusCode();
|
||||
|
||||
if ($status !== 200) {
|
||||
$body = (string) $response->getBody();
|
||||
throw new ApiErrorException($body, $status);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
|
||||
if (property_exists($result, 'channels')) {
|
||||
$result->channels = get_object_vars($result->channels);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously trigger multiple events at the same time.
|
||||
*
|
||||
* @param array $batch [optional] An array of events to send
|
||||
* @param bool $already_encoded [optional]
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @throws PusherException
|
||||
*/
|
||||
public function triggerBatchAsync(array $batch = [], bool $already_encoded = false): PromiseInterface
|
||||
{
|
||||
$request = $this->make_batch_request($batch, $already_encoded);
|
||||
|
||||
$promise = $this->client->sendAsync($request, [
|
||||
'http_errors' => false,
|
||||
'base_uri' => $this->channels_url_prefix()
|
||||
])->then(function ($response) {
|
||||
$status = $response->getStatusCode();
|
||||
|
||||
if ($status !== 200) {
|
||||
$body = (string) $response->getBody();
|
||||
throw new ApiErrorException($body, $status);
|
||||
}
|
||||
|
||||
$result = json_decode($response->getBody(), false, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
if (property_exists($result, 'channels')) {
|
||||
$result->channels = get_object_vars($result->channels);
|
||||
}
|
||||
|
||||
return $result;
|
||||
});
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch channel information for a specific channel.
|
||||
*
|
||||
* @param string $channel The name of the channel
|
||||
* @param array $params Additional parameters for the query e.g. $params = array( 'info' => 'connection_count' )
|
||||
*
|
||||
* @throws PusherException If $channel is invalid
|
||||
* @throws ApiErrorException Throws ApiErrorException if the Channels HTTP API responds with an error
|
||||
* @throws GuzzleException
|
||||
*
|
||||
*/
|
||||
public function getChannelInfo(string $channel, array $params = []): object
|
||||
{
|
||||
$this->validate_channel($channel);
|
||||
|
||||
return $this->get('/channels/' . $channel, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favour of getChannelInfo
|
||||
*/
|
||||
public function get_channel_info(string $channel, array $params = []): object
|
||||
{
|
||||
return $this->getChannelInfo($channel, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list containing all channels.
|
||||
*
|
||||
* @param array $params Additional parameters for the query e.g. $params = array( 'info' => 'connection_count' )
|
||||
*
|
||||
* @throws ApiErrorException Throws ApiErrorException if the Channels HTTP API responds with an error
|
||||
* @throws GuzzleException
|
||||
*
|
||||
*/
|
||||
public function getChannels(array $params = []): object
|
||||
{
|
||||
$result = $this->get('/channels', $params);
|
||||
|
||||
$result->channels = get_object_vars($result->channels);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favour of getChannels
|
||||
*/
|
||||
public function get_channels(array $params = []): object
|
||||
{
|
||||
return $this->getChannels($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch user ids currently subscribed to a presence channel.
|
||||
*
|
||||
* @param string $channel The name of the channel
|
||||
*
|
||||
* @throws ApiErrorException Throws ApiErrorException if the Channels HTTP API responds with an error
|
||||
* @throws GuzzleException
|
||||
*
|
||||
*/
|
||||
public function getPresenceUsers(string $channel): object
|
||||
{
|
||||
return $this->get('/channels/' . $channel . '/users');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favour of getPresenceUsers
|
||||
*/
|
||||
public function get_users_info(string $channel): object
|
||||
{
|
||||
return $this->getPresenceUsers($channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET arbitrary REST API resource using a synchronous http client.
|
||||
* All request signing is handled automatically.
|
||||
*
|
||||
* @param string $path Path excluding /apps/APP_ID
|
||||
* @param array $params API params (see http://pusher.com/docs/rest_api)
|
||||
* @param bool $associative When true, return the response body as an associative array, else return as an object
|
||||
*
|
||||
* @throws ApiErrorException Throws ApiErrorException if the Channels HTTP API responds with an error
|
||||
* @throws GuzzleException
|
||||
* @throws PusherException
|
||||
*
|
||||
* @return mixed See Pusher API docs
|
||||
*/
|
||||
public function get(string $path, array $params = [], $associative = false)
|
||||
{
|
||||
$path = $this->settings['base_path'] . $path;
|
||||
|
||||
$signature = $this->sign($path, 'GET', $params);
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Pusher-Library' => 'pusher-http-php ' . self::$VERSION
|
||||
];
|
||||
|
||||
$response = $this->client->get($path, [
|
||||
'query' => $signature,
|
||||
'http_errors' => false,
|
||||
'headers' => $headers,
|
||||
'base_uri' => $this->channels_url_prefix()
|
||||
]);
|
||||
|
||||
$status = $response->getStatusCode();
|
||||
|
||||
if ($status !== 200) {
|
||||
$body = (string) $response->getBody();
|
||||
throw new ApiErrorException($body, $status);
|
||||
}
|
||||
|
||||
try {
|
||||
$body = json_decode($response->getBody(), $associative, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data decoding error.');
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a socket signature.
|
||||
*
|
||||
* @param string $channel
|
||||
* @param string $socket_id
|
||||
* @param string|null $custom_data
|
||||
*
|
||||
* @return string Json encoded authentication string.
|
||||
* @throws PusherException Throws exception if $channel is invalid or above or $socket_id is invalid
|
||||
*/
|
||||
public function socketAuth(string $channel, string $socket_id, string $custom_data = null): string
|
||||
{
|
||||
$this->validate_channel($channel);
|
||||
$this->validate_socket_id($socket_id);
|
||||
|
||||
if ($custom_data) {
|
||||
$signature = hash_hmac('sha256', $socket_id . ':' . $channel . ':' . $custom_data, $this->settings['secret'], false);
|
||||
} else {
|
||||
$signature = hash_hmac('sha256', $socket_id . ':' . $channel, $this->settings['secret'], false);
|
||||
}
|
||||
|
||||
$signature = ['auth' => $this->settings['auth_key'] . ':' . $signature];
|
||||
// add the custom data if it has been supplied
|
||||
if ($custom_data) {
|
||||
$signature['channel_data'] = $custom_data;
|
||||
}
|
||||
|
||||
if (PusherCrypto::is_encrypted_channel($channel)) {
|
||||
if (!is_null($this->crypto)) {
|
||||
$signature['shared_secret'] = base64_encode($this->crypto->generate_shared_secret($channel));
|
||||
} else {
|
||||
throw new PusherException('You must specify an encryption master key to authorize an encrypted channel');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$response = json_encode($signature, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favour of socketAuth
|
||||
*/
|
||||
public function socket_auth(string $channel, string $socket_id, string $custom_data = null): string
|
||||
{
|
||||
return $this->socketAuth($channel, $socket_id, $custom_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a presence signature (an extension of socket signing).
|
||||
*
|
||||
* @param string $channel
|
||||
* @param string $socket_id
|
||||
* @param string $user_id
|
||||
* @param mixed $user_info
|
||||
*
|
||||
* @return string
|
||||
* @throws PusherException Throws exception if $channel is invalid or above or $socket_id is invalid
|
||||
*/
|
||||
public function presenceAuth(string $channel, string $socket_id, string $user_id, $user_info = null): string
|
||||
{
|
||||
$user_data = ['user_id' => $user_id];
|
||||
if ($user_info) {
|
||||
$user_data['user_info'] = $user_info;
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->socket_auth($channel, $socket_id, json_encode($user_data, JSON_THROW_ON_ERROR));
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favour of presenceAuth
|
||||
*/
|
||||
public function presence_auth(string $channel, string $socket_id, string $user_id, $user_info = null): string
|
||||
{
|
||||
return $this->presenceAuth($channel, $socket_id, $user_id, $user_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a webhook actually came from Pusher, decrypts any encrypted events, and marshals them into a PHP object.
|
||||
*
|
||||
* @param array $headers a array of headers from the request (for example, from getallheaders())
|
||||
* @param string $body the body of the request (for example, from file_get_contents('php://input'))
|
||||
*
|
||||
* @throws PusherException
|
||||
*
|
||||
* @return Webhook marshalled object with the properties time_ms (an int) and events (an array of event objects)
|
||||
*/
|
||||
public function webhook(array $headers, string $body): object
|
||||
{
|
||||
$this->ensure_valid_signature($headers, $body);
|
||||
|
||||
$decoded_events = [];
|
||||
try {
|
||||
$decoded_json = json_decode($body, false, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
$this->log('Unable to decrypt webhook event payload.', null, LogLevel::WARNING);
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
|
||||
foreach ($decoded_json->events as $key => $event) {
|
||||
if (PusherCrypto::is_encrypted_channel($event->channel)) {
|
||||
if (!is_null($this->crypto)) {
|
||||
$decryptedEvent = $this->crypto->decrypt_event($event);
|
||||
|
||||
if ($decryptedEvent === false) {
|
||||
$this->log('Unable to decrypt webhook event payload. Wrong key? Ignoring.', null, LogLevel::WARNING);
|
||||
continue;
|
||||
}
|
||||
$decoded_events[] = $decryptedEvent;
|
||||
} else {
|
||||
$this->log('Got an encrypted webhook event payload, but no master key specified. Ignoring.', null, LogLevel::WARNING);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
$decoded_events[] = $event;
|
||||
}
|
||||
}
|
||||
return new Webhook($decoded_json->time_ms, $decoded_events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a given Pusher Signature is valid.
|
||||
*
|
||||
* @param array $headers an array of headers from the request (for example, from getallheaders())
|
||||
* @param string $body the body of the request (for example, from file_get_contents('php://input'))
|
||||
*
|
||||
* @throws PusherException if signature is incorrect.
|
||||
*/
|
||||
public function verifySignature(array $headers, string $body): void
|
||||
{
|
||||
$x_pusher_key = $headers['X-Pusher-Key'];
|
||||
$x_pusher_signature = $headers['X-Pusher-Signature'];
|
||||
if ($x_pusher_key === $this->settings['auth_key']) {
|
||||
$expected = hash_hmac('sha256', $body, $this->settings['secret']);
|
||||
if ($expected === $x_pusher_signature) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new PusherException(sprintf('Received WebHook with invalid signature: got %s.', $x_pusher_signature));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favour of verifySignature
|
||||
*/
|
||||
public function ensure_valid_signature(array $headers, string $body): void
|
||||
{
|
||||
$this->verifySignature($headers, $body);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace Pusher;
|
||||
|
||||
class PusherCrypto
|
||||
{
|
||||
private $encryption_master_key;
|
||||
|
||||
// The prefix any e2e channel must have
|
||||
public const ENCRYPTED_PREFIX = 'private-encrypted-';
|
||||
|
||||
/**
|
||||
* Checks if a given channel is an encrypted channel.
|
||||
*
|
||||
* @param string $channel the name of the channel
|
||||
*
|
||||
* @return bool true if channel is an encrypted channel
|
||||
*/
|
||||
public static function is_encrypted_channel(string $channel): bool
|
||||
{
|
||||
return strpos($channel, self::ENCRYPTED_PREFIX) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $encryption_master_key_base64
|
||||
* @return string
|
||||
* @throws PusherException
|
||||
*/
|
||||
public static function parse_master_key($encryption_master_key_base64): string
|
||||
{
|
||||
if (!function_exists('sodium_crypto_secretbox')) {
|
||||
throw new PusherException('To use end to end encryption, you must either be using PHP 7.2 or greater or have installed the libsodium-php extension for php < 7.2.');
|
||||
}
|
||||
|
||||
if ($encryption_master_key_base64 !== '') {
|
||||
$decoded_key = base64_decode($encryption_master_key_base64, true);
|
||||
if ($decoded_key === false) {
|
||||
throw new PusherException('encryption_master_key_base64 must be a valid base64 string');
|
||||
}
|
||||
|
||||
if (strlen($decoded_key) !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
|
||||
throw new PusherException('encryption_master_key_base64 must encode a key which is 32 bytes long');
|
||||
}
|
||||
|
||||
return $decoded_key;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises a PusherCrypto instance.
|
||||
*
|
||||
* @param string $encryption_master_key the SECRET_KEY_LENGTH key that will be used for key derivation.
|
||||
*/
|
||||
public function __construct(string $encryption_master_key)
|
||||
{
|
||||
$this->encryption_master_key = $encryption_master_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a given event.
|
||||
*
|
||||
* @param object $event an object that has an encrypted data property and a channel property.
|
||||
*
|
||||
* @return object the event with a decrypted payload, or false if decryption was unsuccessful.
|
||||
* @throws PusherException
|
||||
*/
|
||||
public function decrypt_event(object $event): object
|
||||
{
|
||||
$parsed_payload = $this->parse_encrypted_message($event->data);
|
||||
$shared_secret = $this->generate_shared_secret($event->channel);
|
||||
$decrypted_payload = $this->decrypt_payload($parsed_payload->ciphertext, $parsed_payload->nonce, $shared_secret);
|
||||
if (!$decrypted_payload) {
|
||||
throw new PusherException('Decryption of the payload failed. Wrong key?');
|
||||
}
|
||||
$event->data = $decrypted_payload;
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a shared secret from the secret key and the channel to broadcast to.
|
||||
*
|
||||
* @param string $channel the name of the channel
|
||||
*
|
||||
* @return string a SHA256 hash (encoded as base64) of the channel name appended to the encryption key
|
||||
* @throws PusherException
|
||||
*/
|
||||
public function generate_shared_secret(string $channel): string
|
||||
{
|
||||
if (!self::is_encrypted_channel($channel)) {
|
||||
throw new PusherException('You must specify a channel of the form private-encrypted-* for E2E encryption. Got ' . $channel);
|
||||
}
|
||||
|
||||
return hash('sha256', $channel . $this->encryption_master_key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a given plaintext for broadcast on a particular channel.
|
||||
*
|
||||
* @param string $channel the name of the channel the payloads event will be broadcast on
|
||||
* @param string $plaintext the data to encrypt
|
||||
*
|
||||
* @return string a string ready to be sent as the data of an event.
|
||||
* @throws PusherException
|
||||
* @throws \SodiumException
|
||||
*/
|
||||
public function encrypt_payload(string $channel, string $plaintext): string
|
||||
{
|
||||
if (!self::is_encrypted_channel($channel)) {
|
||||
throw new PusherException('Cannot encrypt plaintext for a channel that is not of the form private-encrypted-*. Got ' . $channel);
|
||||
}
|
||||
$nonce = $this->generate_nonce();
|
||||
$shared_secret = $this->generate_shared_secret($channel);
|
||||
$cipher_text = sodium_crypto_secretbox($plaintext, $nonce, $shared_secret);
|
||||
|
||||
try {
|
||||
return $this->format_encrypted_message($nonce, $cipher_text);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data encoding error.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a given payload using the nonce and shared secret.
|
||||
*
|
||||
* @param string $payload the ciphertext
|
||||
* @param string $nonce the nonce used in the encryption
|
||||
* @param string $shared_secret the shared_secret used in the encryption
|
||||
*
|
||||
* @return string plaintext
|
||||
* @throws \SodiumException
|
||||
*/
|
||||
public function decrypt_payload(string $payload, string $nonce, string $shared_secret)
|
||||
{
|
||||
$plaintext = sodium_crypto_secretbox_open($payload, $nonce, $shared_secret);
|
||||
if (empty($plaintext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an encrypted message ready for broadcast.
|
||||
*
|
||||
* @param string $nonce the nonce used in the encryption process (bytes)
|
||||
* @param string $ciphertext the ciphertext (bytes)
|
||||
*
|
||||
* @return string JSON with base64 encoded nonce and ciphertext`
|
||||
* @throws \JsonException
|
||||
*/
|
||||
private function format_encrypted_message(string $nonce, string $ciphertext): string
|
||||
{
|
||||
$encrypted_message = new \stdClass();
|
||||
$encrypted_message->nonce = base64_encode($nonce);
|
||||
$encrypted_message->ciphertext = base64_encode($ciphertext);
|
||||
|
||||
return json_encode($encrypted_message, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an encrypted message into its nonce and ciphertext components.
|
||||
*
|
||||
*
|
||||
* @param string $payload the encrypted message payload
|
||||
*
|
||||
* @return object php object with decoded nonce and ciphertext
|
||||
* @throws PusherException
|
||||
*/
|
||||
private function parse_encrypted_message(string $payload): object
|
||||
{
|
||||
try {
|
||||
$decoded_payload = json_decode($payload, false, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new PusherException('Data decoding error.');
|
||||
}
|
||||
|
||||
$decoded_payload->nonce = base64_decode($decoded_payload->nonce);
|
||||
$decoded_payload->ciphertext = base64_decode($decoded_payload->ciphertext);
|
||||
if ($decoded_payload->ciphertext === '' || strlen($decoded_payload->nonce) !== SODIUM_CRYPTO_SECRETBOX_NONCEBYTES) {
|
||||
throw new PusherException('Received a payload that cannot be parsed.');
|
||||
}
|
||||
|
||||
return $decoded_payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a nonce that is SODIUM_CRYPTO_SECRETBOX_NONCEBYTES long.
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function generate_nonce(): string
|
||||
{
|
||||
return random_bytes(
|
||||
SODIUM_CRYPTO_SECRETBOX_NONCEBYTES
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Pusher;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PusherException extends Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Pusher;
|
||||
|
||||
class PusherInstance
|
||||
{
|
||||
private static $instance = null;
|
||||
private static $app_id = '';
|
||||
private static $secret = '';
|
||||
private static $api_key = '';
|
||||
|
||||
/**
|
||||
* Get the pusher singleton instance.
|
||||
*
|
||||
* @return Pusher
|
||||
* @throws PusherException
|
||||
*/
|
||||
public static function get_pusher()
|
||||
{
|
||||
if (self::$instance !== null) {
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
self::$instance = new Pusher(
|
||||
self::$api_key,
|
||||
self::$secret,
|
||||
self::$app_id
|
||||
);
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user