├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.xml
├── build
├── phpcs-ruleset-win.xml
├── phpcs-ruleset.xml
├── phpdox.xml
└── phpmd.xml
├── codeception.yml
├── composer.json
├── composer.lock
├── docs
├── application.md
├── assets.md
├── custard
│ └── index.md
├── date-time.md
├── dependency-injection.md
├── deployment.md
├── encryption.md
├── events.md
├── exceptions.md
├── files-and-directories.md
├── filters-and-layout.md
├── http-clients.md
├── logging.md
├── login.md
├── modules.md
├── processing-overview.md
├── recordstreams.md
├── request.md
├── response-generating.md
├── response.md
├── sessions.md
├── settings.md
├── simple-xml-transcoder.md
├── static-resources.md
├── string-tools.md
├── toc.txt
└── url-handlers.md
├── phpunit.xml
├── platform
├── boot-application.php
├── boot-rhubarb.php
├── execute-cli.php
├── execute-http.php
├── execute-test.php
└── standard-development-apache-rewrites.conf
├── resources
├── resource-manager.js
└── validation.js
├── src
├── Application.php
├── Assets
│ ├── Asset.php
│ ├── AssetCatalogueProvider.php
│ ├── AssetCatalogueSettings.php
│ ├── AssetUrlHandler.php
│ ├── LocalStorageAssetCatalogueProvider.php
│ ├── LocalStorageAssetCatalogueProviderSettings.php
│ ├── LoginValidatedAssetUrlHandler.php
│ └── TempLocalStorageAssetCatalogueProvider.php
├── DataStreams
│ ├── CsvStream.php
│ ├── RecordStream.php
│ └── XmlStream.php
├── DateTime
│ ├── RhubarbDate.php
│ ├── RhubarbDateInterval.php
│ ├── RhubarbDateTime.php
│ └── RhubarbTime.php
├── DependencyInjection
│ ├── Container.php
│ ├── ProviderInterface.php
│ ├── ProviderTrait.php
│ ├── SingletonInterface.php
│ ├── SingletonProviderTrait.php
│ └── SingletonTrait.php
├── Deployment
│ ├── Deployable.php
│ ├── DeploymentPackage.php
│ ├── RelocationResourceDeploymentProvider.php
│ ├── ResourceDeploymentPackage.php
│ └── ResourceDeploymentProvider.php
├── Encryption
│ ├── Aes256ComputedKeyEncryptionProvider.php
│ ├── Aes256EncryptionProvider.php
│ ├── EncryptionProvider.php
│ ├── HashProvider.php
│ ├── PlainTextHashProvider.php
│ └── Sha512HashProvider.php
├── Events
│ ├── Event.php
│ └── EventEmitter.php
├── Exceptions
│ ├── AssetException.php
│ ├── AssetExposureException.php
│ ├── AssetNotFoundException.php
│ ├── AttemptToModifyReadOnlyPropertyException.php
│ ├── ClassMappingException.php
│ ├── CollectionUrlException.php
│ ├── DeploymentException.php
│ ├── EmailException.php
│ ├── EndOfStreamException.php
│ ├── FileNotFoundException.php
│ ├── ForceResponseException.php
│ ├── Handlers
│ │ ├── DefaultExceptionHandler.php
│ │ ├── ExceptionHandler.php
│ │ └── ExceptionSettings.php
│ ├── HttpResponseException.php
│ ├── ImplementationException.php
│ ├── NonRhubarbException.php
│ ├── ResourceNotFound.php
│ ├── RhubarbException.php
│ ├── SettingMissingException.php
│ ├── StaticResource404Exception.php
│ ├── StaticResourceNotFoundException.php
│ └── StopGeneratingResponseException.php
├── Html
│ └── ResourceLoader.php
├── Http
│ ├── CurlHttpClient.php
│ ├── HttpClient.php
│ ├── HttpRequest.php
│ └── HttpResponse.php
├── Layout
│ ├── Exceptions
│ │ └── LayoutNotFoundException.php
│ ├── Layout.php
│ ├── LayoutModule.php
│ └── ResponseFilters
│ │ └── LayoutFilter.php
├── Logging
│ ├── IndentedMessageLog.php
│ ├── Log.php
│ ├── MonologLog.php
│ └── PhpLog.php
├── LoginProviders
│ ├── CredentialsLoginProviderInterface.php
│ ├── Exceptions
│ │ ├── CredentialsFailedException.php
│ │ ├── LoginFailedException.php
│ │ └── NotLoggedInException.php
│ ├── LoginProvider.php
│ └── UrlHandlers
│ │ └── ValidateLoginUrlHandler.php
├── Mime
│ ├── MimeDocument.php
│ ├── MimePart.php
│ ├── MimePartBinaryFile.php
│ ├── MimePartCollection.php
│ ├── MimePartImage.php
│ └── MimePartText.php
├── Modelling
│ ├── AllPublicModelState.php
│ └── ModelState.php
├── Module.php
├── PhpContext.php
├── Request
│ ├── BinaryRequest.php
│ ├── CliRequest.php
│ ├── JsonRequest.php
│ ├── MultiPartFormDataRequest.php
│ ├── Request.php
│ ├── WebRequest.php
│ └── XmlRequest.php
├── Response
│ ├── BasicAuthorisationRequiredResponse.php
│ ├── BinaryResponse.php
│ ├── ExpiredResponse.php
│ ├── FileResponse.php
│ ├── GeneratesResponseInterface.php
│ ├── HtmlResponse.php
│ ├── JsonResponse.php
│ ├── NotAuthorisedResponse.php
│ ├── NotFoundResponse.php
│ ├── RedirectResponse.php
│ ├── Response.php
│ ├── TooManyLoginAttemptsResponse.php
│ └── XmlResponse.php
├── ResponseFilters
│ └── ResponseFilter.php
├── Scripts
│ └── ProjectTemplates.php
├── Sendables
│ ├── Email
│ │ ├── Email.php
│ │ ├── EmailProvider.php
│ │ ├── EmailRecipient.php
│ │ ├── EmailSettings.php
│ │ ├── PhpMailEmailProvider.php
│ │ ├── SimpleEmail.php
│ │ └── TemplateEmail.php
│ ├── Sendable.php
│ ├── SendableProvider.php
│ └── SendableRecipient.php
├── Sessions
│ ├── EncryptedSession.php
│ ├── Exceptions
│ │ └── SessionProviderNotFoundException.php
│ ├── Session.php
│ └── SessionProviders
│ │ ├── PhpSessionProvider.php
│ │ └── SessionProvider.php
├── Settings.php
├── Settings
│ ├── HtmlPageSettings.php
│ └── WebsiteSettings.php
├── String
│ ├── StringTools.php
│ └── Template.php
├── UrlHandlers
│ ├── CallableUrlHandler.php
│ ├── ClassMappedUrlHandler.php
│ ├── CollectionUrlHandling.php
│ ├── GreedyUrlHandler.php
│ ├── NamespaceMappedUrlHandler.php
│ ├── NumericGreedyUrlHandler.php
│ ├── StaticResourceUrlHandler.php
│ ├── UrlCapturedDataUrlHandler.php
│ └── UrlHandler.php
└── Xml
│ ├── Node.php
│ ├── NodeStrategy.php
│ ├── NodeStrategyCollation.php
│ ├── NodeStrategyCollationDictionary.php
│ ├── NodeStrategyRead.php
│ ├── NodeStrategyTraversal.php
│ ├── SimpleXmlTranscoder.php
│ └── XmlParser.php
└── tests
├── .gitignore
├── Fixtures
├── Codeception
│ ├── RhubarbConnector.php
│ └── RhubarbFramework.php
├── DependencyInjection
│ ├── DependencyWithArguments.php
│ ├── ExtendedSimpleClass.php
│ ├── OneDependency.php
│ └── SimpleClass.php
├── Emails
│ ├── FancyUnitTestingTemplateEmail.php
│ └── UnitTestingTemplateEmail.php
├── Layout
│ ├── TestLayout.php
│ └── TestLayout2.php
├── LoginProviders
│ └── UnitTestingLoginProvider.php
├── Modules
│ ├── UnitTestingModule.php
│ └── UnitTestingModuleB.php
├── SimpleContent.php
├── TestCases
│ ├── AppTestCase.php
│ ├── RequestTestCase.php
│ └── RhubarbTestCase.php
├── UnitTestingEmailProvider.php
├── UnitTestingRhubarbRequestHttpClient.php
├── UnitTestingSettings.php
└── UrlHandlers
│ ├── DifferentNamespaceTest
│ ├── FindMe.php
│ └── readme.txt
│ ├── NamespaceMappedHandlerTests
│ ├── ObjectA.php
│ └── SubFolder
│ │ ├── Index.php
│ │ └── ObjectB.php
│ ├── TestParentHandler.php
│ ├── UnitTestComputedUrlHandler.php
│ ├── base.css
│ ├── subfolder
│ └── test3.txt
│ ├── test.txt
│ └── test2.txt
├── _bootstrap.php
├── _data
├── _multiPartFormDataRequestUnitTestFile.txt
└── dump.sql
├── _output
└── .gitignore
├── _support
├── AcceptanceTester.php
├── FunctionalTester.php
├── Helper
│ ├── Acceptance.php
│ ├── Functional.php
│ └── Unit.php
├── UnitTester.php
└── _generated
│ ├── AcceptanceTesterActions.php
│ ├── FunctionalTesterActions.php
│ └── UnitTesterActions.php
├── acceptance.suite.yml
├── acceptance
└── _bootstrap.php
├── functional.suite.yml
├── functional
└── _bootstrap.php
├── unit.suite.yml
└── unit
├── ApplicationTest.php
├── Assets
├── .gitignore
├── AssetCatalogueProviderTest.php
├── AssetCatalogueProviderTests.php
├── AssetUrlHandlerTest.php
├── LocalStorageAssetCatalogueProviderTest.php
└── LoginValidatedAssetUrlHandlerTest.php
├── ContainerTest.php
├── DataStreams
├── CsvStreamTest.php
├── RecordStreamTest.php
└── XmlStreamTest.php
├── DateTime
├── RhubarbDateIntervalTest.php
├── RhubarbDateTest.php
├── RhubarbDateTimeTest.php
└── RhubarbTimeTest.php
├── Deployment
├── RelocationResourceDeploymentHandlerTest.php
└── ResourceDeploymentPackageTest.php
├── Encryption
├── Aes256EncryptionProviderTest.php
├── PlainTextHashProviderTest.php
├── Sha512HashProviderTest.php
└── UnitTestingAes256EncryptionProvider.php
├── Events
├── EventEmitterTest.php
└── EventTest.php
├── Exceptions
└── Handlers
│ └── DefaultExceptionHandlerTest.php
├── Html
└── ResourceLoaderTest.php
├── Layout
└── LayoutModuleTest.php
├── Logging
├── IndentedMessageLogTest.php
├── LogTest.php
└── UnitTestLog.php
├── LoginProviders
├── LoginProviderTest.php
└── ValidateLoginUrlHandlerTest.php
├── Mime
└── MimeDocumentTest.php
├── Modelling
└── ModelStateTest.php
├── PhpContextTest.php
├── Request
├── JsonRequestTest.php
├── MultiPartFormDataRequestTest.php
├── RequestTest.php
├── WebRequestSSLOffWindowsTest.php
├── WebRequestSSLOnTest.php
├── WebRequestTest.php
└── XmlRequestTest.php
├── Response
├── HtmlResponseTest.php
├── JsonResponseTest.php
├── RedirectResponseTest.php
├── ResponseTest.php
└── XmlResponseTest.php
├── Sendables
└── Email
│ ├── EmailAddressTest.php
│ ├── EmailTest.php
│ └── TemplateEmailTest.php
├── Sessions
├── EncryptedSessionTest.php
├── SessionProviders
│ └── PhpSessionProviderTest.php
├── SessionTest.php
├── UnitTestingSession.php
└── UnitTestingSessionProvider.php
├── SettingsTest.php
├── String
├── StringToolsTest.php
└── TemplateTest.php
├── UrlHandlers
├── CallableUrlHandlerTest.php
├── ClassMappedHandlerTest.php
├── GreedyUrlHandlerTest.php
├── NamespaceMappedHandlerTest.php
├── NumericGreedyUrlHandlerTest.php
├── StaticResourceTest.php
└── UrlHandlerTestUnitTest.php
├── Xml
├── SimpleXmlTranscoderTest.php
└── XmlParserTest.php
└── _bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.phar
2 | vendor/
3 |
4 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
5 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
6 | # composer.lock
7 | /.idea/
8 | /deployed/
9 | /logs/
10 | /bin/
11 | tests/_output/*
12 | /cache/
13 | /build/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rhubarb PHP
2 |
3 | Rhubarb is an application development framework for PHP. Its focus is on allowing developers to build enterprise ready applications that are fast, scale well, allow for architectural rethinks late in the project and that maximise the potential for code reuse.
4 |
5 | ## Projects and Modules
6 |
7 | Rhubarb is a modular system. Your application only brings in the modules it needs. So if you're building an API you will use the RestAPI module but not the MVP module. This keeps the burden on autoloaders down and makes your application easier to deploy and maintain.
8 |
9 | The main framework resides in the `rhubarb` project. This includes the platform bootstraps and a core set of classes called 'Crown'.
10 |
11 | Rhubarb uses Composer to import additional packages into the solution including it's own modules. To keep our github organisation tidy other Rhubarb modules reside in projects called `module.[modulename]`. For example `module.modelling` or `module.sendgrid`
12 |
13 | ## Contributing
14 |
15 | Rhubarb is an open source project and as such anyone may make a contribution. Contributions can be made by forking any of the rhubarb projects and making a pull request back to the base fork.
16 |
17 | Rhubarb has a list of senior contributors who guard and protect the values of Rhubarb and make the final decision on the merits of each pull request.
18 |
--------------------------------------------------------------------------------
/build/phpcs-ruleset-win.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
27 | A custom coding standard
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | */tests/*
38 |
39 |
40 |
41 | */tests/*
42 |
43 |
44 |
45 | */tests/*
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/build/phpcs-ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
27 | A custom coding standard
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | */tests/*
37 |
38 |
39 |
40 | */tests/*
41 |
42 |
43 |
44 | */tests/*
45 |
46 |
47 |
--------------------------------------------------------------------------------
/build/phpmd.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
25 |
26 | My custom rule set that checks my code...
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/codeception.yml:
--------------------------------------------------------------------------------
1 | actor: Tester
2 | paths:
3 | tests: tests
4 | log: tests/_output
5 | data: tests/_data
6 | support: tests/_support
7 | envs: tests/_envs
8 | settings:
9 | bootstrap: _bootstrap.php
10 | colors: true
11 | memory_limit: 1024M
12 | extensions:
13 | enabled:
14 | - Codeception\Extension\RunFailed
15 | modules:
16 | config:
17 | Db:
18 | dsn: ''
19 | user: ''
20 | password: ''
21 | dump: tests/_data/dump.sql
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rhubarbphp/rhubarb",
3 | "description": "A modern enterprise ready PHP framework",
4 | "keywords": [ "php", "framework" ],
5 | "homepage": "http://www.rhubarbphp.com/",
6 | "license": "Apache-2.0",
7 | "autoload": {
8 | "psr-4": {
9 | "Rhubarb\\Crown\\": "src/",
10 | "Rhubarb\\Crown\\Tests\\": "tests/"
11 | }
12 | },
13 | "require": {
14 | "php": ">=8.0.0",
15 | "firebase/php-jwt": "^4.0 || ^5.0",
16 | "psr/container": "^2.0.0"
17 | },
18 | "require-dev": {
19 | "rhubarbphp/custard": "^1.0.9",
20 | "rhubarbphp/module-build-status-updater": "^1.0.5",
21 | "codeception/codeception": "^2.0.0"
22 | },
23 | "config": {
24 | "bin-dir": "bin/"
25 | },
26 | "scripts": {
27 | "post-create-project-cmd": [
28 | "Rhubarb\\Crown\\Scripts\\ProjectTemplates::createMinimumProject"
29 | ]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/docs/custard/index.md:
--------------------------------------------------------------------------------
1 | Custard
2 | =======
3 |
4 | Custard is an extension of the [Symfony Command](http://symfony.com/doc/current/console.html) object that
5 | provides a familiar interface for running command line scripts on the terminal.
6 |
7 | The Symfony Command object is used by a number of frameworks and composer so
8 | is instantly recognisable in it's usage.
9 |
10 | ## Running Custard
11 |
12 | Custard is a development requirement of the main Rhubarb framework however if you
13 | project needs it for running scripts in a deployed environment you should add it
14 | to your composer.json:
15 |
16 | ``` javascript
17 | "require": {
18 | "rhubarbphp/custard": "^1.0.9"
19 | }
20 | ```
21 |
22 | As per composer's default behaviour the command itself will be found in the vendor
23 | folder on the following path:
24 |
25 | ``` bash
26 | vendor/rhubarbphp/custard/bin/custard
27 | ```
28 |
29 | As it's such a popular tool it's common for projects to tell composer to put binaries
30 | in a folder closer to your top level folder. This is achieved by adding a `bin-dir`
31 | section to your composer.json:
32 |
33 | ``` javascript
34 | "config": {
35 | "bin-dir": "bin/"
36 | }
37 | ```
38 |
39 | Now you can run custard with:
40 |
41 | ``` bash
42 | bin/custard
43 | ```
44 |
45 | This is the form used in examples in this documentation.
46 |
47 | ## Listing available commands.
48 |
49 | Running custard with no arguments returns the list of available commands. Commands
50 | are prefixed with a short 'namespace' which usually hints at the module providing
51 | the command.
52 |
53 | ``` bash
54 | bin/custard
55 | ```
56 |
57 | ## Running a command
58 |
59 | Simply add the name of the command on the end:
60 |
61 | ``` bash
62 | bin/custard stem:document-models
63 | ```
64 |
65 | Many commands require interaction on the terminal to answer questions and some provide
66 | switches and arguments you can supply to skip the interactions. Consult the documentation
67 | for each individual command for those details.
68 |
--------------------------------------------------------------------------------
/docs/http-clients.md:
--------------------------------------------------------------------------------
1 | HTTP Clients
2 | ============
3 |
4 | To make an HTTP client you should try to avoid using PHP libraries such as Curl directly. There are many
5 | open source HTTP client libraries that make the job of integrating with HTTP services much easier. Rhubarb
6 | includes a similar library simply called `HttpClient`.
7 |
8 | HttpClient is an abstract base class that implements the provider pattern. It has one method
9 | `getResponse(HttpRequest $request)` that is supplied by concrete types.
10 |
11 | This approach allows the HTTP client to be swapped with one using a different library or for unit testing
12 | a mock client that can extend the reach of testing with Rhubarb REST APIs.
13 |
14 | The default HTTP client is the CurlHttpClient which as the name suggests uses the Curl library to make its
15 | requests.
16 |
17 | As a provider you can call the `setProviderClassName()` function to set the required provider for your application:
18 |
19 | ``` php
20 | HttpClient::setProviderClassName(GuzzleHttpClient::class);
21 | ```
22 |
23 | To make a request create a `HttpRequest` object, get the client and call `getResponse`:
24 |
25 | ``` php
26 | $myObject = [1, 2, 3];
27 | $request = new HttpRequest('http://api.myservice.com/', 'put', json_encode($myObject));
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/response-generating.md:
--------------------------------------------------------------------------------
1 | Generating a Response
2 | =====================
3 |
4 | To generate a response a class should implement the `GeneratesResponseInterface` interface and define the
5 | 'generateResponse()' method.
6 |
7 | `UrlHandler` objects implement this interface however they usually instantiate a specialist class that generates
8 | the real response.
9 |
10 | The class should return a `Response` object, normally a `HtmlResponse` for web requests or a `JsonResponse` for a
11 | REST API.
12 |
13 | A simple response generator might look like this:
14 |
15 | ``` php
16 | class GreetingResponder implements GeneratesResponseInterface
17 | {
18 | public function generateResponse(Request $request)
19 | {
20 | $response = new HtmlResponse();
21 | $response->setContent("
Welcome friend!
");
22 |
23 | return $response;
24 | }
25 | }
26 | ```
27 |
28 | To connect this with a URL you can use the `ClassMappedUrlHandler`. For example here we configure it to
29 | serve the homepage of our site:
30 |
31 | ``` php
32 | // In registerUrlHandlers of your module class:
33 | $this->addUrlHandlers(
34 | [
35 | "/" => new ClassMappedUrlHandler(GreetingResponser::class)
36 | ]);
37 | ```
38 |
39 | Creating HTML directly in a response generating object is something usually reserved for very simple, non interactive
40 | use cases. For normal interactive screen design it's better to use a design pattern like MVP which you can find
41 | in the [leaf](/manual/module.leaf/) module.
42 |
--------------------------------------------------------------------------------
/docs/sessions.md:
--------------------------------------------------------------------------------
1 | Sessions
2 | ===
3 |
4 | In the Rhubarb user sessions are managed through Session objects. Session objects handle the content of a session
5 | while session providers handle the storage and retrieval of sessions.
6 |
7 | Much like settings sessions are scoped into individually named classes. A single application might have multiple
8 | session objects in use at one time handling different session data. It's even possible for these different
9 | session objects to be using different session providers and thereby storing the session data in different ways.
10 |
11 | ~~~ php
12 | class LoginSession extends Session
13 | {
14 | public bool $loggedIn;
15 |
16 | public string $username;
17 | }
18 |
19 | $loginSession = LoginSession::singleton();
20 | $loginSession->loggedIn = true;
21 | ~~~
22 |
23 | The `Session` class extends the `Settings` class and as you've seen is used in a very similar way.
24 |
25 | To store the session for recovery on the next page make a call to `storeSession()`
26 |
27 | ~~~ php
28 | $loginSession->storeSession();
29 | ~~~
30 |
31 | ## Changing the default session provider
32 |
33 | The default session provider is `PhpSessionProvider` which will store and recover session data using the standard
34 | PHP session functions. To change the default provider call the static method `SessionProvider::setProviderClassName()`
35 | from your application configuration passing the name of the class to be used. Like all provider setup calls we pass
36 | the name of the class and not an instance of it so that scripts which don't require sessions won't waste
37 | resources with objects they don't need.
38 |
39 | ## Changing the session provider for an individual session class
40 |
41 | You can have different sessions using different session providers. Simply override the `getNewSessionProvider()`
42 | method of your individual session provider.
43 |
44 | ~~~ php
45 | class LoginSession extends Session
46 | {
47 | protected function getNewSessionProvider()
48 | {
49 | return new ModelSessionProvider();
50 | }
51 | }
52 | ~~~
53 |
--------------------------------------------------------------------------------
/docs/simple-xml-transcoder.md:
--------------------------------------------------------------------------------
1 | Simple XML Transcoder
2 | ============
3 |
4 | The simple xml transcoder is designed to be an XML equivalent of php's built in `json_encode` / `json_decode` functions.
5 |
6 | ## Usage
7 |
8 | This helper class provides two methods. Each method should behave exactly like it's json equivalent,
9 | but with XML input/output strings.
10 |
11 | ### Encode
12 |
13 | `\Rhubarb\Crown\Xml\SimpleXmlTranscoder::encode($mixed)`
14 |
15 | ### Decode
16 |
17 | `\Rhubarb\Crown\Xml\SimpleXmlTranscoder::decode($mixed, $objectsToAssociativeArrays = false)`
18 |
19 | ## XML Vocabulary
20 |
21 | ### XMLNS
22 |
23 | Any node names or attributes which the decoder relies on will be use the `rbrb` namespace.
24 |
25 | ### Type Attribute
26 |
27 | Denotes how the contents of a node should be decoded.
28 | Types can be set either using the `type` attribute on a named node,
29 | or as the actual node name if the node has no other meaningful name (eg the root node).
30 |
31 | Supported types:
32 | * `obj` denotes an object. Expect child nodes to be named as the object's property names.
33 | Node Values are the property values.
34 | * `arr` denotes an array. Each child node is an entry in the array. Child node names are inconsequential,
35 | but will try to take the single form of a plural array node name when using the `encode` method.
36 | * `num` the data type of this node's value should be treated as a number
37 | * `bool` the data type of this node's value should be treated as a boolean. Possible value: `true`/`false`
38 |
--------------------------------------------------------------------------------
/docs/static-resources.md:
--------------------------------------------------------------------------------
1 | Static Resources
2 | ================
3 |
4 | Static resources like images, Javascript, stylesheets and documents can be served by Rhubarb using the
5 | `StaticResourceUrlHandler`. This handler can be created with the path to either a single file or a directory
6 | of files:
7 |
8 | ``` php
9 | $this->addUrlHandlers(
10 | [
11 | // A single file
12 | "/static/robots.txt" => new StaticResourceUrlHandler(__DIR__."/../static/robots.txt"),
13 |
14 | // A directory of files
15 | "/static/images" => new StaticResourceUrlHandler(__DIR__."/../static/images")
16 | ]);
17 | ```
18 |
19 | As static resources require no processing by PHP much higher performance can be achieved by putting
20 | URL rewriting rules to bypass Rhubarb directly into your web server configuration. For example in Apache
21 | the following rules would achieve the same result as above but will be many times faster:
22 |
23 | ```
24 | RewriteRule ^/static/robots.txt %{DOCUMENT_ROOT}/static/robots.txt [QSA,L,NC]
25 | RewriteRule ^/static/images/$1 %{DOCUMENT_ROOT}/static/images/$1 [QSA,L,NC]
26 | ```
27 |
28 | To avoid proliferating the configuration file with rewrite rules it's common to find two main rewrite rules
29 | for serving static files:
30 |
31 | ```
32 | RewriteRule ^/static/(.+) %{DOCUMENT_ROOT}/static/$1 [QSA,L,NC]
33 | RewriteRule ^/deployed/(.+) %{DOCUMENT_ROOT}/deployed/$1 [QSA,L,NC]
34 | ```
35 |
36 | A common set of rewrite rules for Apache can be found in the
37 | `vendor/rhubarbphp/rhubarb/platform/standard-development-rewrites.conf` directory.
38 |
39 | Most project level static resources are then served under the `static` directory. The `deployed` directory is
40 | a requirement of the [RelocationResourceDeploymentProvider](deployment#content) class.
41 |
42 | > Remember! A production deployed project should not be serving static resources through a UrlHandler.
--------------------------------------------------------------------------------
/docs/toc.txt:
--------------------------------------------------------------------------------
1 | Basic Concepts:
2 | . Files and Directory Layout:files-and-directories
3 | . Request Processing Overview:processing-overview
4 | . The Application Object:application
5 | . Modules:modules
6 | . Generating a Response:response-generating
7 | . URL Handlers:url-handlers
8 | . Requests:request
9 | . Responses:response
10 | . Filters and Layout:filters-and-layout
11 | . Dependency Injection:dependency-injection
12 | . Static Resources:static-resources
13 | Common Patterns:
14 | . Settings:settings
15 | . Sessions:sessions
16 | . Handling Logins:login
17 | . Sendables:sendables
18 | . Encryption:encryption
19 | . Deploying Resources:deployment
20 | . Asset Storage:assets
21 | . Logging:logging
22 | . Handling Exceptions:exceptions
23 | . Dates and Times:date-time
24 | . Events:events
25 | . Record Streams:recordstreams
26 | . Modules and Scaffolds:modules-and-scaffolds
27 | . Http Clients:http-clients
28 | Toolkit Classes:
29 | . StringTools:string-tools
30 | . Mime:mime
31 | . Xml:xml
32 | Custard:
33 | . Introduction & Setup:custard/
34 | . Creating a custard command:custard/make-your-own
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
23 |
24 |
25 | tests
26 |
27 |
28 |
--------------------------------------------------------------------------------
/platform/boot-application.php:
--------------------------------------------------------------------------------
1 | initialiseModules();
30 | } else {
31 | // We need an application object for dependency injection and the developer hasn't given us one
32 | // Create an empty application as a back stop.
33 | $application = new \Rhubarb\Crown\Application();
34 | }
35 |
--------------------------------------------------------------------------------
/platform/boot-rhubarb.php:
--------------------------------------------------------------------------------
1 | loginProviderClassName = $loginProviderClassName;
44 | }
45 |
46 | protected function isPermitted()
47 | {
48 | $providerClass = $this->loginProviderClassName;
49 |
50 | if ($providerClass != ""){
51 | $provider = $providerClass::singleton();
52 | } else {
53 | $provider = LoginProvider::getProvider();
54 | }
55 |
56 | return $provider->isLoggedIn();
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/src/Assets/TempLocalStorageAssetCatalogueProvider.php:
--------------------------------------------------------------------------------
1 | readNextItem()) {
38 | $this->appendItem($item);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/DateTime/RhubarbTime.php:
--------------------------------------------------------------------------------
1 | setDate(self::$yearMustAlwaysBe, self::$monthMustAlwaysBe, self::$dayMustAlwaysBe);
38 | }
39 |
40 | /**
41 | * We never want to do comparisons on date - therefore we always force the date time to have the same date
42 | * @param int $year
43 | * @param int $month
44 | * @param int $day
45 | *
46 | * @return \DateTime
47 | */
48 | public function setDate($year, $month, $day)
49 | {
50 | return parent::setDate(self::$yearMustAlwaysBe, self::$monthMustAlwaysBe, self::$dayMustAlwaysBe);
51 | }
52 |
53 | function __toString()
54 | {
55 | return $this->format("H:i:s");
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/DependencyInjection/ProviderInterface.php:
--------------------------------------------------------------------------------
1 | registerClass(static::class, $providerClassName);
25 | }
26 |
27 | /**
28 | * @return static
29 | */
30 | public static function getProvider()
31 | {
32 | return Container::instance(static::class);
33 | }
34 | }
--------------------------------------------------------------------------------
/src/DependencyInjection/SingletonInterface.php:
--------------------------------------------------------------------------------
1 | registerClass(static::class, $providerClassName, true);
27 | }
28 | }
--------------------------------------------------------------------------------
/src/DependencyInjection/SingletonTrait.php:
--------------------------------------------------------------------------------
1 | getSingleton(static::class, function() use ($arguments) {
29 | return new static(...$arguments);
30 | });
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Deployment/Deployable.php:
--------------------------------------------------------------------------------
1 | onDeploy();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Deployment/ResourceDeploymentPackage.php:
--------------------------------------------------------------------------------
1 | resourcesToDeploy as $path) {
40 | $urls[] = $deploymentHandler->getDeployedResourceUrl($path);
41 | }
42 |
43 | return $urls;
44 | }
45 |
46 | protected function onDeploy()
47 | {
48 | $deploymentHandler = ResourceDeploymentProvider::getProvider();
49 |
50 | $urls = [];
51 |
52 | foreach ($this->resourcesToDeploy as $path) {
53 | $urls[] = $deploymentHandler->deployResource($path);
54 | }
55 |
56 | return $urls;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Deployment/ResourceDeploymentProvider.php:
--------------------------------------------------------------------------------
1 | getEncryptionKey($keySalt);
36 |
37 | return openssl_encrypt($data, "aes-256-cbc", $key, false, "3132333435363738");
38 | }
39 |
40 | public function decrypt($data, $keySalt = "")
41 | {
42 | $key = $this->getEncryptionKey($keySalt);
43 |
44 | return openssl_decrypt($data, "aes-256-cbc", $key, false, "3132333435363738");
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Encryption/EncryptionProvider.php:
--------------------------------------------------------------------------------
1 | token = $token;
40 | }
41 |
42 | /**
43 | * Gets the token associated with this exception.
44 | *
45 | * @return string
46 | */
47 | public function getToken()
48 | {
49 | return $this->token;
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Exceptions/AssetExposureException.php:
--------------------------------------------------------------------------------
1 | response = $response;
37 | }
38 |
39 | /**
40 | * Get's the response object.
41 | *
42 | * @return \Rhubarb\Crown\Response\Response
43 | */
44 | public function getResponse()
45 | {
46 | return $this->response;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Exceptions/Handlers/ExceptionSettings.php:
--------------------------------------------------------------------------------
1 | response = $response;
32 | $this->request = $request;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Exceptions/ImplementationException.php:
--------------------------------------------------------------------------------
1 | getMessage());
32 |
33 | $this->line = $nonRhubarbException->getLine();
34 | $this->file = $nonRhubarbException->getFile();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Exceptions/ResourceNotFound.php:
--------------------------------------------------------------------------------
1 | publicMessage;
39 | }
40 |
41 | /**
42 | * Returns the private message attached to this exception.
43 | *
44 | * @return string
45 | */
46 | public function getPrivateMessage()
47 | {
48 | return $this->getMessage();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Exceptions/SettingMissingException.php:
--------------------------------------------------------------------------------
1 | printLayout($response->getContent());
41 |
42 | $fullHtml = ob_get_clean();
43 |
44 | $response->setContent($fullHtml);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Layout/ResponseFilters/LayoutFilter.php:
--------------------------------------------------------------------------------
1 | processResponse($response);
53 |
54 | return $response;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Logging/IndentedMessageLog.php:
--------------------------------------------------------------------------------
1 | writeFormattedEntry($level, $message, $category, $additionalData);
36 | }
37 |
38 | /**
39 | * The logger should implement this method to perform the actual log committal.
40 | *
41 | * @param int $level The log level
42 | * @param string $message The text message to log
43 | * @param string $category The category of log message
44 | * @param array $additionalData Any number of additional key value pairs which can be understood by specific
45 | * logs (e.g. an API log might understand what AuthenticationToken means)
46 | * @return mixed
47 | */
48 | abstract protected function writeFormattedEntry($level, $message, $category, $additionalData);
49 | }
50 |
--------------------------------------------------------------------------------
/src/LoginProviders/CredentialsLoginProviderInterface.php:
--------------------------------------------------------------------------------
1 | publicMessage = "Sorry, we failed to authenticate your credentials at this time.";
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/LoginProviders/Exceptions/NotLoggedInException.php:
--------------------------------------------------------------------------------
1 | publicMessage = "Sorry, you must be logged in to complete this action.";
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Mime/MimePartBinaryFile.php:
--------------------------------------------------------------------------------
1 | headers["Content-Type"] = $mimeType;
28 | $this->headers["Content-Transfer-Encoding"] = "base64";
29 | }
30 |
31 | /**
32 | * Creates an instance to represent a file stored locally.
33 | *
34 | * @param $path
35 | * @param string $name
36 | * @return MimePartBinaryFile
37 | */
38 | public static function fromLocalPath($path, $name = "")
39 | {
40 | if ($name == "") {
41 | $name = basename($path);
42 | }
43 |
44 | $part = new MimePartBinaryFile();
45 | $part->setTransformedBody(file_get_contents($path));
46 | $part->addHeader("Content-Disposition", "attachment; filename=\"" . $name . "\"");
47 |
48 | return $part;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Mime/MimePartCollection.php:
--------------------------------------------------------------------------------
1 | headers["Content-Type"] = $mimeType;
28 | $this->headers["Content-Transfer-Encoding"] = "base64";
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Mime/MimePartText.php:
--------------------------------------------------------------------------------
1 | headers["Content-Type"] = $mimeType;
28 | $this->headers["Content-Transfer-Encoding"] = "quoted-printable";
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Modelling/AllPublicModelState.php:
--------------------------------------------------------------------------------
1 | modelData);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Request/BinaryRequest.php:
--------------------------------------------------------------------------------
1 | getRequestBody();
28 |
29 | return $requestBody;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Request/CliRequest.php:
--------------------------------------------------------------------------------
1 | getOriginatingPhpContext();
38 | $requestBody = trim($context->getRequestBody());
39 |
40 | return json_decode($requestBody, $this->objectsToAssocArrays);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Request/XmlRequest.php:
--------------------------------------------------------------------------------
1 | getOriginatingPhpContext();
12 | $requestBody = trim($context->getRequestBody());
13 |
14 | return SimpleXmlTranscoder::decode($requestBody, $this->objectsToAssocArrays);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Response/BasicAuthorisationRequiredResponse.php:
--------------------------------------------------------------------------------
1 | setHeader("WWW-authenticate", "Basic realm=\"" . $realm . "\"");
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Response/BinaryResponse.php:
--------------------------------------------------------------------------------
1 | binaryData = $binaryData;
29 | $this->setHeader('Content-Type', $contentType);
30 |
31 | if ($fileName != "") {
32 | $this->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"');
33 | }
34 |
35 | $this->setHeader('Content-Transfer-Encoding', 'binary');
36 | $this->setHeader('Expires', '0');
37 | $this->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
38 | $this->setHeader('Pragma', 'public');
39 | $this->setHeader('Content-Length', strlen($binaryData));
40 | }
41 |
42 | public function getBinaryData()
43 | {
44 | return $this->binaryData;
45 | }
46 |
47 | protected function printContent()
48 | {
49 | @ob_clean();
50 |
51 | print $this->binaryData;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Response/ExpiredResponse.php:
--------------------------------------------------------------------------------
1 | setHeader("WWW-authenticate", "Basic realm=\"" . $realm . "\"");
14 |
15 | $this->setResponseCode(Response::HTTP_STATUS_CLIENT_ERROR_FORBIDDEN);
16 | $this->setResponseMessage("403 Forbidden");
17 |
18 | $this->setContent("Sorry, your account has now expired.");
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Response/GeneratesResponseInterface.php:
--------------------------------------------------------------------------------
1 | setHeader('Content-Type', 'text/html');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Response/JsonResponse.php:
--------------------------------------------------------------------------------
1 | setHeader('Content-Type', 'application/json');
36 | }
37 |
38 | public function formatContent()
39 | {
40 | return json_encode($this->getContent());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Response/NotAuthorisedResponse.php:
--------------------------------------------------------------------------------
1 | setResponseCode(Response::HTTP_STATUS_CLIENT_ERROR_UNAUTHORIZED);
28 | $this->setResponseMessage("401 Unauthorized");
29 |
30 | $this->setContent("Sorry, you are not authorised to access this resource.");
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Response/NotFoundResponse.php:
--------------------------------------------------------------------------------
1 | setResponseCode(Response::HTTP_STATUS_CLIENT_ERROR_NOT_FOUND);
28 | $this->setResponseMessage("Not Found");
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Response/TooManyLoginAttemptsResponse.php:
--------------------------------------------------------------------------------
1 | setHeader("WWW-authenticate", "Basic realm=\"" . $realm . "\"");
14 |
15 | $this->setResponseCode(Response::HTTP_STATUS_CLIENT_ERROR_UNAUTHORIZED);
16 | $this->setResponseMessage("401 Unauthorized");
17 |
18 | $this->setContent("Sorry, your account has been disabled due too many failed login attempts.");
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Response/XmlResponse.php:
--------------------------------------------------------------------------------
1 | setHeader('Content-Type', 'text/xml');
33 | }
34 |
35 | public function formatContent()
36 | {
37 | if($this->content != '' && !is_string($this->content)) {
38 | return SimpleXmlTranscoder::encode(parent::formatContent());
39 | }
40 | return parent::formatContent();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/ResponseFilters/ResponseFilter.php:
--------------------------------------------------------------------------------
1 | request();
44 | $host = $request->server("SERVER_NAME");
45 |
46 | $this->defaultSender = new EmailRecipient("donotreply@" . $host . ".com");
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Sendables/Email/PhpMailEmailProvider.php:
--------------------------------------------------------------------------------
1 | getRecipientList();
31 |
32 | Log::debug("Sending message to ".$recipientList,"EMAIL");
33 |
34 | mail(
35 | $recipientList,
36 | $email->getSubject(),
37 | $email->getBodyRaw(),
38 | $email->getMailHeadersAsString(),
39 | "-f".$email->getSender()->email
40 | );
41 |
42 | Log::indent();
43 | Log::debug("Sent message to ".$recipientList,"EMAIL");
44 | Log::outdent();
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Sendables/SendableProvider.php:
--------------------------------------------------------------------------------
1 | send($sendable);
45 | }
46 |
47 | /**
48 | * @param Sendable $sendable
49 | * @return SendableProvider
50 | */
51 | private static function createProviderForSendable(Sendable $sendable)
52 | {
53 | $providerClass = $sendable->getProviderClassName();
54 | $provider = $providerClass::getProvider();
55 |
56 | return $provider;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Sendables/SendableRecipient.php:
--------------------------------------------------------------------------------
1 | needsInitialised) {
38 | $this->needsInitialised = false;
39 | $this->initialiseDefaultValues();
40 | }
41 | }
42 |
43 | /**
44 | * Override this class to set default values for settings.
45 | */
46 | protected function initialiseDefaultValues()
47 | {
48 |
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Settings/HtmlPageSettings.php:
--------------------------------------------------------------------------------
1 | $match) {
44 | if (isset($data[$matches[1][$key]])) {
45 | $template = str_replace($match, $data[$matches[1][$key]], $template);
46 | } else if (!$keepPlaceholders) {
47 | $template = str_replace($match, '', $template);
48 | }
49 | }
50 | }
51 |
52 | return $template;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/UrlHandlers/ClassMappedUrlHandler.php:
--------------------------------------------------------------------------------
1 | className = $className;
34 | }
35 |
36 | public function setClassName($className)
37 | {
38 | $this->className = $className;
39 | }
40 |
41 | protected function createHandlingClass()
42 | {
43 | $class = $this->className;
44 | $object = new $class();
45 |
46 | return $object;
47 | }
48 |
49 | public function generateResponseForRequest($request = null)
50 | {
51 | $object = $this->createHandlingClass();
52 |
53 | return $object->generateResponse($request);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/UrlHandlers/NumericGreedyUrlHandler.php:
--------------------------------------------------------------------------------
1 | nodeHandlers[ $nodeName ] = $strategy;
30 |
31 | return $this;
32 | }
33 |
34 | public function parse(\XMLReader $xmlReader, $startingDepth = 0, $parseOne = false)
35 | {
36 | parent::parse($xmlReader, $startingDepth, $parseOne);
37 |
38 | // Keep scanning elements while we have elements to scan and we are still within our scope
39 | // namely that the depth is greater than our own depth.
40 | while ($xmlReader->read() && ( $xmlReader->depth > $startingDepth )) {
41 | if ($xmlReader->nodeType == \XMLReader::ELEMENT) {
42 | foreach ($this->nodeHandlers as $name => $strategy) {
43 | if ($name == "*" || $name == $xmlReader->name) {
44 | $strategy->parse($xmlReader, $xmlReader->depth);
45 |
46 | if ($parseOne) {
47 | return true;
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
54 | return false;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 | /cache/
2 |
--------------------------------------------------------------------------------
/tests/Fixtures/Codeception/RhubarbFramework.php:
--------------------------------------------------------------------------------
1 | client = new RhubarbConnector();
29 | }
30 |
31 | public function seeHeader($header, $headerValue = null)
32 | {
33 | $response = $this->client->getInternalResponse();
34 | PHPUnit_Framework_Assert::assertContains($header, $response->getHeaders());
35 |
36 | if ($headerValue != null) {
37 | PHPUnit_Framework_Assert::assertEquals($headerValue, $response->getHeader($header));
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/Fixtures/DependencyInjection/DependencyWithArguments.php:
--------------------------------------------------------------------------------
1 | argument1 = $argument1;
30 | $this->argument2 = $argument2;
31 | }
32 | }
--------------------------------------------------------------------------------
/tests/Fixtures/DependencyInjection/ExtendedSimpleClass.php:
--------------------------------------------------------------------------------
1 | injected = $simpleClass;
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/Fixtures/DependencyInjection/SimpleClass.php:
--------------------------------------------------------------------------------
1 | {Content}";
33 | }
34 |
35 | protected function GetTextLayout()
36 | {
37 | return "abc{Content}def";
38 | }
39 | }
--------------------------------------------------------------------------------
/tests/Fixtures/Emails/UnitTestingTemplateEmail.php:
--------------------------------------------------------------------------------
1 | Top= $content; ?>TailloggedIn = true;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/Fixtures/Modules/UnitTestingModuleB.php:
--------------------------------------------------------------------------------
1 | UnitTesting = true;
35 |
36 | HttpClient::setDefaultHttpClientClassName(UnitTestingRhubarbRequestHttpClient::class);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/Fixtures/UnitTestingEmailProvider.php:
--------------------------------------------------------------------------------
1 | SettingWithDefault = "default";
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/Fixtures/UrlHandlers/DifferentNamespaceTest/FindMe.php:
--------------------------------------------------------------------------------
1 | setContent("parent");
39 |
40 | return $response;
41 | }
42 |
43 | /**
44 | * Should be implemented to return a true or false as to whether this handler supports the given request.
45 | *
46 | * Normally this involves testing the request URI.
47 | *
48 | * @param \Rhubarb\Crown\Request\Request $request
49 | * @param string $currentUrlFragment
50 | * @return bool
51 | */
52 | protected function getMatchingUrlFragment(Request $request, $currentUrlFragment = "")
53 | {
54 | return (stripos($currentUrlFragment, $this->stub) === 0);
55 | }
56 | }
--------------------------------------------------------------------------------
/tests/Fixtures/UrlHandlers/UnitTestComputedUrlHandler.php:
--------------------------------------------------------------------------------
1 | Content of a.html.
16 |
17 | ----=_NextPart_01CF23F5.37621C10--
18 |
--------------------------------------------------------------------------------
/tests/_data/dump.sql:
--------------------------------------------------------------------------------
1 | /* Replace this file with actual dump of your database */
--------------------------------------------------------------------------------
/tests/_output/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/tests/_support/AcceptanceTester.php:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 | Breakfast
33 | 100
34 |
35 |
36 | Dinner
37 | 200
38 |
39 |
40 | Lunch
41 | 300
42 |
43 |
44 | ');
45 |
46 | @unlink("cache/unit-test-csv-output-from-xml.csv");
47 |
48 | $stream = new XmlStream("meal", "cache/unit-test-xml-stream.xml");
49 | $csvStream = new CsvStream("cache/unit-test-csv-output-from-xml.csv");
50 |
51 | $csvStream->appendStream($stream);
52 |
53 | $this->assertFileExists("cache/unit-test-csv-output-from-xml.csv");
54 |
55 | $content = file_get_contents("cache/unit-test-csv-output-from-xml.csv");
56 | $content = str_replace("\r\n", "\n", $content);
57 |
58 | $this->assertEquals("name,calories\nBreakfast,100\nDinner,200\nLunch,300", $content);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tests/unit/DataStreams/XmlStreamTest.php:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 | Breakfast
33 |
34 |
35 | Dinner
36 |
37 |
38 | Lunch
39 |
40 |
41 | ');
42 |
43 | $stream = new XmlStream("meal", "cache/unit-test-xml-stream.xml");
44 | $meal = $stream->readNextItem();
45 |
46 | $this->assertEquals("Breakfast", $meal["name"]);
47 |
48 | $stream->readNextItem();
49 | $stream->readNextItem();
50 | $meal = $stream->readNextItem();
51 |
52 | $this->assertFalse($meal);
53 |
54 | // This will crash if the file handle isn't released
55 | //unlink( "cache/unit-test-xml-stream.xml" );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/unit/Encryption/Aes256EncryptionProviderTest.php:
--------------------------------------------------------------------------------
1 | encrypt("somedata");
33 |
34 | $this->assertEquals("PrHKcixewGccUfgyQsMWjg==", $result);
35 | }
36 |
37 | public function testDecrypts()
38 | {
39 | $encrypter = new UnitTestingAes256EncryptionProvider();
40 | $result = $encrypter->encrypt("somedata");
41 | $original = $encrypter->decrypt($result);
42 |
43 | $this->assertEquals("somedata", $original);
44 | }
45 | }
--------------------------------------------------------------------------------
/tests/unit/Encryption/PlainTextHashProviderTest.php:
--------------------------------------------------------------------------------
1 | createHash("abc123", "");
29 |
30 | $this->assertEquals("abc123", $result);
31 |
32 | $this->assertTrue($plainTextProvider->compareHash("abc123", "abc123"));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/unit/Encryption/Sha512HashProviderTest.php:
--------------------------------------------------------------------------------
1 | createHash("abc123", "saltyfish");
29 |
30 | $this->assertEquals(
31 | '$6$rounds=10000$saltyfish$xsdN77OODY/XmxLdlkFW9CNxuE4H6NjEGG7K7tGJbzHUyDrVDHROL/FqG.ANet3dcd6WqGOOvaDjLv/WeAtcK0',
32 | $result
33 | );
34 | }
35 |
36 | public function testHashesAreCompared()
37 | {
38 | $hasher = new Sha512HashProvider();
39 |
40 | $hash = $hasher->createHash("abc123", "saltyfish");
41 |
42 | $result = $hasher->compareHash("abc123", $hash);
43 | $this->assertTrue($result);
44 |
45 | $result = $hasher->compareHash("dep456", $hash);
46 | $this->assertFalse($result);
47 |
48 | // Repeat the tests with an automated salt.
49 | $hash = $hasher->createHash("abc123");
50 |
51 | $result = $hasher->compareHash("abc123", $hash);
52 | $this->assertTrue($result);
53 |
54 | $result = $hasher->compareHash("dep456", $hash);
55 | $this->assertFalse($result);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/unit/Encryption/UnitTestingAes256EncryptionProvider.php:
--------------------------------------------------------------------------------
1 | forceLogin();
29 |
30 | $this->assertTrue($loginProvider->isLoggedIn());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/unit/Request/JsonRequestTest.php:
--------------------------------------------------------------------------------
1 | context = $this->application->context();
35 | $this->context->simulateNonCli = true;
36 |
37 | $_SERVER["CONTENT_TYPE"] = "application/json";
38 | }
39 |
40 | public function testPayload()
41 | {
42 | $testPayload =
43 | [
44 | "a" => 1,
45 | "b" => 2
46 | ];
47 |
48 | $this->context->simulatedRequestBody = json_encode($testPayload);
49 |
50 | $request = $this->application->request();
51 |
52 | $this->assertEquals($testPayload, $request->getPayload());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/tests/unit/Request/MultiPartFormDataRequestTest.php:
--------------------------------------------------------------------------------
1 | serverData["REQUEST_METHOD"] = 'PUT';
20 |
21 | $request->setUnitTestRequestFile(__DIR__ . '/../../_data/_multiPartFormDataRequestUnitTestFile.txt');
22 | self::assertTrue(is_array($request->getPayload()));
23 | self::assertCount(3, $request->getPayload());
24 |
25 | $_SERVER["CONTENT_TYPE"] = $originalContentType;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/unit/Request/RequestTest.php:
--------------------------------------------------------------------------------
1 | testEnvKey] = 42;
36 |
37 | $this->request = new WebRequest();
38 | }
39 |
40 | protected function tearDown()
41 | {
42 | unset($_ENV[$this->testEnvKey]);
43 |
44 | parent::tearDown();
45 | }
46 |
47 | public function testGettingOfSuperGlobals()
48 | {
49 | $this->assertEquals($this->testEnvValue, $this->request->env($this->testEnvKey));
50 | $this->assertEquals("defaultValue", $this->request->env("default", "defaultValue"));
51 | }
52 | }
--------------------------------------------------------------------------------
/tests/unit/Request/WebRequestSSLOffWindowsTest.php:
--------------------------------------------------------------------------------
1 | request = new WebRequest();
49 | }
50 |
51 | protected function tearDown()
52 | {
53 | $this->request = null;
54 |
55 | parent::tearDown();
56 | }
57 |
58 | public function testNoSSL()
59 | {
60 | $this->assertFalse($this->request->isSSL());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/unit/Request/WebRequestSSLOnTest.php:
--------------------------------------------------------------------------------
1 | request = new WebRequest();
46 | }
47 |
48 | protected function tearDown()
49 | {
50 | $this->request = null;
51 |
52 | parent::tearDown();
53 | }
54 |
55 | public function testSSL()
56 | {
57 | $this->assertTrue($this->request->isSSL());
58 | }
59 | }
--------------------------------------------------------------------------------
/tests/unit/Request/XmlRequestTest.php:
--------------------------------------------------------------------------------
1 | context = $this->application->context();
21 | $this->context->simulateNonCli = true;
22 |
23 | $_SERVER['CONTENT_TYPE'] = 'text/xml';
24 | }
25 |
26 | public function testRequestType()
27 | {
28 | self::assertInstanceOf(XmlRequest::class, $this->application->request());
29 | $this->setUp();
30 | $_SERVER['CONTENT_TYPE'] = 'application/xml';
31 | self::assertInstanceOf(XmlRequest::class, $this->application->request());
32 | }
33 |
34 | public function testPayload()
35 | {
36 | $testPayload =
37 | [
38 | 'a' => 1,
39 | 'b' => 2
40 | ];
41 |
42 | $context = $this->application->context();
43 | $context->simulatedRequestBody = SimpleXmlTranscoder::encode($testPayload);
44 |
45 | $request = $this->application->request();
46 |
47 | $this->assertEquals($testPayload, $request->getPayload());
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/unit/Response/HtmlResponseTest.php:
--------------------------------------------------------------------------------
1 | response = new HtmlResponse();
30 | }
31 |
32 | protected function tearDown()
33 | {
34 | $this->response = null;
35 | }
36 |
37 | public function testDefaultContentTypeHeader()
38 | {
39 | $this->assertEquals(
40 | $this->response->getHeaders()['Content-Type'],
41 | 'text/html',
42 | "Content-Type header is not set to text/html"
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/tests/unit/Response/JsonResponseTest.php:
--------------------------------------------------------------------------------
1 | Forename = "abc";
29 | $test->Surname = "123";
30 | $response->setContent($test);
31 |
32 | ob_start();
33 | $response->send();
34 | $buffer = ob_get_clean();
35 |
36 | $this->assertEquals('{"Forename":"' . $test->Forename . '","Surname":"123"}', $buffer);
37 | }
38 |
39 | public function testResponseCanCodeNonModels()
40 | {
41 | $response = new JsonResponse();
42 | $test = ["abc", "123"];
43 | $response->setContent($test);
44 |
45 | ob_start();
46 | $response->send();
47 | $buffer = ob_get_clean();
48 |
49 | $this->assertEquals('["abc","123"]', $buffer);
50 |
51 | $response = new JsonResponse();
52 | $test = new \stdClass();
53 | $test->abc = "123";
54 |
55 | $response->setContent($test);
56 |
57 | ob_start();
58 | $response->send();
59 | $buffer = ob_get_clean();
60 |
61 | $this->assertEquals('{"abc":"123"}', $buffer);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/unit/Response/RedirectResponseTest.php:
--------------------------------------------------------------------------------
1 | getHeaders();
30 |
31 | $this->assertEquals("/go/to/here", $headers["Location"]);
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/unit/Response/XmlResponseTest.php:
--------------------------------------------------------------------------------
1 | setContent('some string content');
15 | ob_start();
16 | $response->send();
17 | $buffer = ob_get_clean();
18 | self::assertEquals('some string content', $buffer);
19 |
20 | $response->setContent([1,2,3]);
21 | ob_start();
22 | $response->send();
23 | $buffer = ob_get_clean();
24 | self::assertEquals(SimpleXmlTranscoder::encode([1,2,3]), $buffer);
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/unit/Sendables/Email/TemplateEmailTest.php:
--------------------------------------------------------------------------------
1 | "Fairbanks", "Age" => "21++", "HairColour" => "brown"]);
30 |
31 | $this->assertEquals("Your name is Fairbanks", $email->getText());
32 | $this->assertEquals("Your age is 21++", $email->getHtml());
33 | $this->assertEquals("Your hair is brown", $email->getSubject());
34 |
35 | $email = new FancyUnitTestingTemplateEmail(["Name" => "Fairbanks", "Age" => "21++", "HairColour" => "brown"]);
36 |
37 | $this->assertEquals("Your age is 21++
", $email->getHtml(), "Templated emails using layouts aren't using the html layout");
38 | $this->assertEquals("abcYour name is Fairbanksdef", $email->getText(), "Templated emails using layouts aren't using the text layout");
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/tests/unit/Sessions/SessionProviders/PhpSessionProviderTest.php:
--------------------------------------------------------------------------------
1 | TestValue = "abc123";
31 | $session->storeSession();
32 |
33 | $this->assertEquals("abc123", $_SESSION['Rhubarb\Crown\Tests\unit\Sessions\UnitTestingSession']["TestValue"]);
34 | }
35 |
36 | public function testSessionRestore()
37 | {
38 | $session = UnitTestingSession::singleton();
39 | $session->TestValue = "abc123";
40 | $session->storeSession();
41 |
42 | Container::current()->clearSingleton(UnitTestingSession::class);
43 |
44 | $session = UnitTestingSession::singleton();
45 |
46 | $this->assertEquals("abc123", $session->TestValue);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/unit/Sessions/SessionTest.php:
--------------------------------------------------------------------------------
1 | registerClass(SessionProvider::class, UnitTestingSessionProvider::class);
35 |
36 | $session = UnitTestingSession::singleton();
37 |
38 | $this->assertInstanceOf(UnitTestingSessionProvider::class, $session->testGetSessionProvider());
39 |
40 | Container::current()->registerClass(SessionProvider::class, PhpSessionProvider::class);
41 |
42 | // Although we have changed the default provider, we already instantiated the session so the provider will not
43 | // have changed
44 | $this->assertInstanceOf(UnitTestingSessionProvider::class, $session->testGetSessionProvider());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/unit/Sessions/UnitTestingSession.php:
--------------------------------------------------------------------------------
1 | getSessionProvider();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/unit/Sessions/UnitTestingSessionProvider.php:
--------------------------------------------------------------------------------
1 | assertEquals("default", $settings->SettingWithDefault);
37 |
38 | $settings->SettingWithDefault = "abc";
39 |
40 | $settings = UnitTestingSettings::singleton();
41 |
42 | $this->assertEquals("abc", $settings->SettingWithDefault);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/unit/String/TemplateTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($plainTextTemplate, Template::parseTemplate($plainTextTemplate, []));
30 |
31 | $template = "Ah something to process! {Forename}";
32 |
33 | $this->assertEquals(
34 | "Ah something to process! Andrew",
35 | Template::parseTemplate($template, ["Forename" => "Andrew"])
36 | );
37 |
38 | $template = "Something to parse, but that you can't parse, because there is no {data}";
39 |
40 | $this->assertEquals(
41 | $template, Template::parseTemplate($template, [], true)
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/unit/UrlHandlers/ClassMappedHandlerTest.php:
--------------------------------------------------------------------------------
1 | urlPath = "/wrong/path/";
32 |
33 | $handler = new ClassMappedUrlHandler(TestTarget::class);
34 | $handler->setUrl("/right/path/");
35 |
36 | $response = $handler->generateResponse($request);
37 |
38 | $this->assertFalse($response);
39 |
40 | $request = new WebRequest();
41 | $request->urlPath = "/right/path/";
42 |
43 | $response = $handler->generateResponse($request);
44 |
45 | $this->assertEquals("bing bang bong", $response->getContent());
46 | }
47 | }
48 |
49 | class TestTarget implements GeneratesResponseInterface
50 | {
51 | public function generateResponse($request = null)
52 | {
53 | $response = new Response();
54 | $response->setContent("bing bang bong");
55 |
56 | return $response;
57 | }
58 | }
--------------------------------------------------------------------------------
/tests/unit/_bootstrap.php:
--------------------------------------------------------------------------------
1 |