Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f01dcb9fa | ||
|
|
1c02eabb7c | ||
|
|
b2f0562a7a | ||
|
|
5fab953fbd | ||
|
|
abe0ae741f | ||
|
|
55238aa169 | ||
|
|
fdb3658cd0 | ||
|
|
bfddb60d6d | ||
|
|
aee28b3450 | ||
|
|
db87a5d45f | ||
|
|
6efcb252e1 | ||
|
|
0c54185f1f | ||
|
|
b325fff158 | ||
|
|
6d3092c736 | ||
|
|
236bdc258d | ||
|
|
8721901494 | ||
|
|
a34a951b97 | ||
|
|
0c48352677 |
No files matched your search
@@ -0,0 +1 @@
|
||||
github: Mattitja AB
|
||||
@@ -0,0 +1,66 @@
|
||||
name: Bug Report
|
||||
description: Report an Issue or Bug with the Package
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
We're sorry to hear you have a problem. Can you help us solve it by providing the following details.
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: What did you expect to happen?
|
||||
placeholder: I cannot currently do X thing because when I do, it breaks X thing.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: how-to-reproduce
|
||||
attributes:
|
||||
label: How to reproduce the bug
|
||||
description: How did this occur, please add any config values used and provide a set of reliable steps if possible.
|
||||
placeholder: When I do X I see Y.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: package-version
|
||||
attributes:
|
||||
label: Package Version
|
||||
description: What version of our Package are you running? Please be as specific as possible
|
||||
placeholder: 2.0.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: php-version
|
||||
attributes:
|
||||
label: PHP Version
|
||||
description: What version of PHP are you running? Please be as specific as possible
|
||||
placeholder: 8.2.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: laravel-version
|
||||
attributes:
|
||||
label: Laravel Version
|
||||
description: What version of Laravel are you running? Please be as specific as possible
|
||||
placeholder: 9.0.0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: operating-systems
|
||||
attributes:
|
||||
label: Which operating systems does this happen with?
|
||||
description: You may select more than one.
|
||||
multiple: true
|
||||
options:
|
||||
- macOS
|
||||
- Windows
|
||||
- Linux
|
||||
- type: textarea
|
||||
id: notes
|
||||
attributes:
|
||||
label: Notes
|
||||
description: Use this field to provide any other notes that you feel might be relevant to the issue.
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Ask a question
|
||||
url: https://github.com/mattitjaab/laravel-cision-feed/discussions/new?category=q-a
|
||||
about: Ask the community for help
|
||||
- name: Request a feature
|
||||
url: https://github.com/mattitjaab/laravel-cision-feed/discussions/new?category=ideas
|
||||
about: Share ideas for new features
|
||||
- name: Report a security issue
|
||||
url: https://github.com/mattitjaab/laravel-cision-feed/security/policy
|
||||
about: Learn how to notify us for sensitive bugs
|
||||
@@ -0,0 +1,19 @@
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
|
||||
- package-ecosystem: "composer"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
@@ -0,0 +1,33 @@
|
||||
name: dependabot-auto-merge
|
||||
on: pull_request_target
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
steps:
|
||||
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v2.4.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Auto-merge Dependabot PRs for semver-minor updates
|
||||
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}
|
||||
run: gh pr merge --auto --merge "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
- name: Auto-merge Dependabot PRs for semver-patch updates
|
||||
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
|
||||
run: gh pr merge --auto --merge "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Fix PHP code style issues
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**.php'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
php-code-styling:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Fix PHP code style issues
|
||||
uses: aglipanci/laravel-pint-action@2.5
|
||||
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v6
|
||||
with:
|
||||
commit_message: Fix styling
|
||||
@@ -0,0 +1,28 @@
|
||||
name: PHPStan
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**.php'
|
||||
- 'phpstan.neon.dist'
|
||||
- '.github/workflows/phpstan.yml'
|
||||
|
||||
jobs:
|
||||
phpstan:
|
||||
name: phpstan
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.4'
|
||||
coverage: none
|
||||
|
||||
- name: Install composer dependencies
|
||||
uses: ramsey/composer-install@v3
|
||||
|
||||
- name: Run PHPStan
|
||||
run: ./vendor/bin/phpstan --error-format=github
|
||||
@@ -0,0 +1,77 @@
|
||||
name: run-tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**.php'
|
||||
- '.github/workflows/run-tests.yml'
|
||||
- 'phpunit.xml.dist'
|
||||
- 'composer.json'
|
||||
- 'composer.lock'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 5
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
php: [8.3, 8.4]
|
||||
laravel: [11.*, 12.*]
|
||||
stability: [prefer-lowest, prefer-stable]
|
||||
include:
|
||||
- laravel: 11.*
|
||||
php: 8.3
|
||||
testbench: 9.*
|
||||
carbon: ^2.63
|
||||
- laravel: 11.*
|
||||
php: 8.4
|
||||
testbench: 9.*
|
||||
carbon: ^2.63
|
||||
- laravel: 12.*
|
||||
php: 8.3
|
||||
testbench: 10.*
|
||||
carbon: ^3.8.4
|
||||
- laravel: 12.*
|
||||
php: 8.4
|
||||
testbench: 10.*
|
||||
carbon: ^3.8.4
|
||||
|
||||
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
|
||||
coverage: none
|
||||
|
||||
- name: Setup problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: Remove Larastan if Laravel 12
|
||||
if: startsWith(matrix.laravel, '12.')
|
||||
run: composer remove larastan/larastan --dev --no-update || true
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
composer require \
|
||||
"laravel/framework:${{ matrix.laravel }}" \
|
||||
"orchestra/testbench:${{ matrix.testbench }}" \
|
||||
"nesbot/carbon:${{ matrix.carbon }}" \
|
||||
--no-interaction --no-update
|
||||
|
||||
composer update --${{ matrix.stability }} --prefer-dist --no-interaction
|
||||
|
||||
- name: List Installed Dependencies
|
||||
run: composer show -D
|
||||
|
||||
- name: Execute tests
|
||||
run: vendor/bin/pest --ci
|
||||
@@ -0,0 +1,32 @@
|
||||
name: "Update Changelog"
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Update Changelog
|
||||
uses: stefanzweifel/changelog-updater-action@v1
|
||||
with:
|
||||
latest-version: ${{ github.event.release.name }}
|
||||
release-notes: ${{ github.event.release.body }}
|
||||
|
||||
- name: Commit updated CHANGELOG
|
||||
uses: stefanzweifel/git-auto-commit-action@v6
|
||||
with:
|
||||
branch: main
|
||||
commit_message: Update CHANGELOG
|
||||
file_pattern: CHANGELOG.md
|
||||
@@ -0,0 +1,6 @@
|
||||
.idea
|
||||
.DS_Store
|
||||
vendor
|
||||
composer.lock
|
||||
.phpunit.cache
|
||||
build
|
||||
@@ -0,0 +1,3 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to `laravel-cision-feed` will be documented in this file.
|
||||
@@ -1,15 +1,16 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
Copyright (c) Mattitja AB <hej@mattitja.se>
|
||||
|
||||
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:
|
||||
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 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,
|
||||
@@ -18,4 +19,3 @@ 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.
|
||||
|
||||
+66
-9
@@ -1,24 +1,81 @@
|
||||
{
|
||||
"name": "mathiaspalmqvist/laravel-cision-feed",
|
||||
"description": "Fetches press releases and financial reports from any public company on cision.",
|
||||
"name": "mattitjaab/laravel-cision-feed",
|
||||
"description": "Fetch and parse Cision press releases via RSS and article HTML in Laravel.",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"cision",
|
||||
"press releases",
|
||||
"rss",
|
||||
"html parsing",
|
||||
"package"
|
||||
],
|
||||
"homepage": "https://github.com/mattitjaab/laravel-cision-feed",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mathias Palmqvist",
|
||||
"email": "mathias@mattitja.se"
|
||||
"name": "Mattitja AB",
|
||||
"email": "hej@mattitja.se",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {},
|
||||
"require": {
|
||||
"php": "^8.3",
|
||||
"illuminate/contracts": "^10.0 || ^11.0 || ^12.0",
|
||||
"spatie/laravel-package-tools": "^1.16",
|
||||
"symfony/css-selector": "7.4.x-dev",
|
||||
"symfony/dom-crawler": "7.4.x-dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^8.22 || ^9.0 || ^10.0",
|
||||
"pestphp/pest": "^3.0",
|
||||
"pestphp/pest-plugin-laravel": "^3.0",
|
||||
"pestphp/pest-plugin-arch": "^3.0",
|
||||
"laravel/pint": "^1.14",
|
||||
"nunomaduro/collision": "^8.1 || ^7.10",
|
||||
"larastan/larastan": "^2.9 || ^3.0",
|
||||
"phpstan/phpstan-phpunit": "^1.3 || ^2.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.1 || ^2.0",
|
||||
"phpstan/extension-installer": "^1.3 || ^2.0",
|
||||
"spatie/laravel-ray": "^1.35"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mathiaspalmqvist\\LaravelCisionFeed\\": "src/"
|
||||
"Mattitja\\Cision\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Mattitja\\Cision\\Tests\\": "tests/",
|
||||
"Workbench\\App\\": "workbench/app/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/pest",
|
||||
"test-coverage": "vendor/bin/pest --coverage",
|
||||
"analyse": "vendor/bin/phpstan analyse",
|
||||
"format": "vendor/bin/pint"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Mathiaspalmqvist\\LaravelCisionFeed\\LaravelCisionFeedProvider"
|
||||
]
|
||||
"Mattitja\\Cision\\CisionServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Cision": "Mattitja\\Cision\\Facades\\Cision"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"phpstan/extension-installer": true,
|
||||
"pestphp/pest-plugin": true
|
||||
}
|
||||
},
|
||||
"support": {
|
||||
"issues": "https://github.com/mattitjaab/laravel-cision-feed/issues",
|
||||
"source": "https://github.com/mattitjaab/laravel-cision-feed"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
Whitespace-only changes.
@@ -0,0 +1,10 @@
|
||||
includes:
|
||||
- phpstan-baseline.neon
|
||||
|
||||
parameters:
|
||||
level: 5
|
||||
paths:
|
||||
- src
|
||||
tmpDir: build/phpstan
|
||||
checkOctaneCompatibility: true
|
||||
checkModelProperties: true
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
executionOrder="random"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
failOnEmptyTestSuite="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
backupStaticProperties="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="MattitjaAB Test Suite">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<junit outputFile="build/report.junit.xml"/>
|
||||
</logging>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
@@ -1,20 +1,90 @@
|
||||
## How to install
|
||||
1. Install the package `composer require mathiaspalmqvist/laravel-cision-feed`
|
||||
2. add the cision company slug to `LARAVEL_CISION_FEED_SLUG` at your env
|
||||
# Laravel Cision Feed
|
||||
|
||||
## Functions
|
||||
[](https://packagist.org/packages/mattitjaab/laravel-cision-feed)
|
||||
[](https://github.com/mattitjaab/laravel-cision-feed/actions?query=workflow%3Arun-tests+branch%3Amain)
|
||||
[](https://github.com/mattitjaab/laravel-cision-feed/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)
|
||||
[](https://packagist.org/packages/mattitjaab/laravel-cision-feed)
|
||||
|
||||
`LaravelCisionFeed::all();`
|
||||
Returns everyting from the feed.
|
||||
A Laravel package for retrieving and parsing data from [Cision News](https://news.cision.com/se). Fetch press releases, financial reports, media posts, and structured article content using a simple and expressive API.
|
||||
|
||||
`LaravelCisionFeed::press();`
|
||||
Returns all press releases from the feed.
|
||||
---
|
||||
|
||||
`LaravelCisionFeed::financial();`
|
||||
Returns all financial reports from the feed.
|
||||
## Features
|
||||
|
||||
`LaravelCisionFeed::news();`
|
||||
Returns all news from the feed.
|
||||
- Retrieve Cision RSS feeds by type (Press, News, Financial, Media)
|
||||
- Parse article pages and extract clean structured HTML
|
||||
- Automatically handles encoding, formatting, and clean-up of content
|
||||
- Fully testable and extensible
|
||||
|
||||
`LaravelCisionFeed::media();`
|
||||
Returns all media from the feed.
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
composer require mattitjaab/laravel-cision-feed
|
||||
```
|
||||
|
||||
You may optionally set your Cision slug in your `.env`:
|
||||
|
||||
```
|
||||
LARAVEL_CISION_FEED_SLUG=your-cision-slug
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
```php
|
||||
use Mattitja\Cision\Cision;
|
||||
|
||||
$cision = new Cision();
|
||||
|
||||
// Fetch RSS entries
|
||||
$items = $cision->press(); // or ->news(), ->financial(), ->media()
|
||||
|
||||
// Fetch full content for a specific article
|
||||
$article = $cision->fetchContent('https://news.cision.com/se/example-company/r/your-article-slug,c1234567');
|
||||
|
||||
echo $article['header']; // Article headline
|
||||
echo $article['published_at']; // ISO timestamp
|
||||
echo $article['content']; // Clean HTML
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
composer test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
See [CHANGELOG](CHANGELOG.md) for recent changes.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please see [CONTRIBUTING](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any security-related issues, please refer to our [security policy](../../security/policy).
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
- [Mattitja AB](https://github.com/MattitjaAB)
|
||||
- [All Contributors](../../contributors)
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). See [LICENSE](LICENSE.md) for details.
|
||||
Executable
+154
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Mattitja\Cision;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Client\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Mattitja\Cision\Support\HtmlCleaner;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
class Cision
|
||||
{
|
||||
protected string $slug;
|
||||
|
||||
/**
|
||||
* Constructor that accepts a slug identifier for the feed.
|
||||
*/
|
||||
public function __construct(?string $slug = null)
|
||||
{
|
||||
$this->slug = $slug ?? env('LARAVEL_CISION_FEED_SLUG', '');
|
||||
}
|
||||
|
||||
public function all(int $page = 1): array|false
|
||||
{
|
||||
return $this->fetch($page);
|
||||
}
|
||||
|
||||
public function press(int $page = 1): array|false
|
||||
{
|
||||
return $this->fetch($page, 'Press');
|
||||
}
|
||||
|
||||
public function financial(int $page = 1): array|false
|
||||
{
|
||||
return $this->fetch($page, 'Financial');
|
||||
}
|
||||
|
||||
public function news(int $page = 1): array|false
|
||||
{
|
||||
return $this->fetch($page, 'News');
|
||||
}
|
||||
|
||||
public function media(int $page = 1): array|false
|
||||
{
|
||||
return $this->fetch($page, 'Media');
|
||||
}
|
||||
|
||||
protected function fetch(int $page = 1, ?string $type = null): array|false
|
||||
{
|
||||
$response = $this->makeRequest($page, $type);
|
||||
|
||||
if ($response === false || $response->failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->parseResponse($response->body(), $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and parse the HTML content of a Cision article URL.
|
||||
*/
|
||||
public function fetchContent(string $url): array
|
||||
{
|
||||
$html = Http::get($url)->body();
|
||||
$crawler = new Crawler($html);
|
||||
|
||||
$article = $crawler->filter('article')->first();
|
||||
|
||||
$header = trim($article->filter('h1')->text());
|
||||
$publishedAt = $article->filter('time')->attr('datetime');
|
||||
|
||||
$allowedTags = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li'];
|
||||
$allowedInline = ['strong', 'b', 'em', 'i', 'br'];
|
||||
|
||||
$content = [];
|
||||
|
||||
foreach ($article as $node) {
|
||||
foreach ($node->childNodes as $child) {
|
||||
$content[] = HtmlCleaner::renderNode($child, $allowedTags, $allowedInline);
|
||||
}
|
||||
}
|
||||
|
||||
$cleaned = HtmlCleaner::clean(implode("\n", array_filter($content)));
|
||||
|
||||
return [
|
||||
'header' => $header,
|
||||
'published_at' => $publishedAt,
|
||||
'content' => $cleaned,
|
||||
];
|
||||
}
|
||||
|
||||
protected function makeRequest(int $page, ?string $type = null): Response|false
|
||||
{
|
||||
if (empty($this->slug)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = [
|
||||
'n' => $this->slug,
|
||||
'pageIx' => $page,
|
||||
'format' => 'rss',
|
||||
];
|
||||
|
||||
if ($type) {
|
||||
$query['m'] = $type;
|
||||
}
|
||||
|
||||
return Http::get('https://news.cision.com/se/ListItems?'.http_build_query($query));
|
||||
}
|
||||
|
||||
protected function parseResponse(string $data, int $page): array|false
|
||||
{
|
||||
if (empty($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Suppress XML parsing warnings
|
||||
libxml_use_internal_errors(true);
|
||||
$xml = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
|
||||
libxml_clear_errors();
|
||||
|
||||
if ($xml === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert XML to JSON then to array
|
||||
$json = json_encode($xml);
|
||||
$rows = json_decode($json, true);
|
||||
|
||||
// Validate structure
|
||||
if (! isset($rows['channel']['item'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure 'item' is always an array
|
||||
$items = $rows['channel']['item'];
|
||||
$items = isset($items[0]) ? $items : [$items];
|
||||
|
||||
// Format the result
|
||||
$formatted = array_map(fn ($item) => [
|
||||
'guid' => $item['guid'],
|
||||
'title' => $item['title'],
|
||||
'description' => $item['description'],
|
||||
'link' => $item['link'],
|
||||
'created_at' => Carbon::parse($item['pubDate'])->addHours(2)->format('Y-m-d H:i:s'),
|
||||
], $items);
|
||||
|
||||
return [
|
||||
'items' => $formatted,
|
||||
'previous_page' => $page > 1 ? $page - 1 : false,
|
||||
'next_page' => count($formatted) === 24 ? $page + 1 : false,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Mattitja\Cision;
|
||||
|
||||
use Spatie\LaravelPackageTools\Package;
|
||||
use Spatie\LaravelPackageTools\PackageServiceProvider;
|
||||
|
||||
class CisionServiceProvider extends PackageServiceProvider
|
||||
{
|
||||
public function configurePackage(Package $package): void
|
||||
{
|
||||
/*
|
||||
* This class is a Package Service Provider
|
||||
*
|
||||
* More info: https://github.com/spatie/laravel-package-tools
|
||||
*/
|
||||
$package
|
||||
->name('laravel-cision-feed');
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Mathiaspalmqvist\LaravelCisionFeed;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class LaravelCisionFeed
|
||||
{
|
||||
/**
|
||||
* @param int $page
|
||||
* @return array|false
|
||||
*/
|
||||
public static function all(int $page = 1): bool|array
|
||||
{
|
||||
$request = self::request($page);
|
||||
|
||||
if ($request->failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::parse($request->body(), $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
* @return array|false
|
||||
*/
|
||||
public static function press(int $page = 1): bool|array
|
||||
{
|
||||
$request = self::request($page, 'Press');
|
||||
|
||||
if ($request->failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::parse($request->body());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
* @return array|false
|
||||
*/
|
||||
public static function financial(int $page = 1): bool|array
|
||||
{
|
||||
$request = self::request($page, 'Financial');
|
||||
|
||||
if ($request->failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::parse($request->body());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
* @return array|false
|
||||
*/
|
||||
public static function news(int $page = 1): bool|array
|
||||
{
|
||||
$request = self::request($page, 'News');
|
||||
|
||||
if ($request->failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::parse($request->body());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
* @return array|false
|
||||
*/
|
||||
public static function media(int $page = 1): bool|array
|
||||
{
|
||||
$request = self::request($page, 'Media');
|
||||
|
||||
if ($request->failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::parse($request->body());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
* @param string|null $m
|
||||
* @return false|\Illuminate\Http\Client\Response
|
||||
*/
|
||||
private static function request(int $page = 1, string $m = null): bool|\Illuminate\Http\Client\Response
|
||||
{
|
||||
if (env('LARAVEL_CISION_FEED_SLUG') === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = [
|
||||
'n' => env('LARAVEL_CISION_FEED_SLUG'),
|
||||
'pageIx' => $page,
|
||||
'format' => 'rss',
|
||||
];
|
||||
|
||||
if ($m) {
|
||||
$query['m'] = $m;
|
||||
}
|
||||
|
||||
return Http::get('https://news.cision.com/se/ListItems?'.http_build_query($query));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $data
|
||||
* @param int $page
|
||||
* @return bool|array
|
||||
*/
|
||||
private static function parse(string $data = null, int $page = 1): bool|array
|
||||
{
|
||||
if ($data === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$xml = simplexml_load_string($data, "SimpleXMLElement", LIBXML_NOCDATA);
|
||||
$json = json_encode($xml);
|
||||
$rows = json_decode($json, true);
|
||||
|
||||
if (! isset($rows['channel']['item'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$return = [];
|
||||
|
||||
foreach ($rows['channel']['item'] as $row) {
|
||||
$return['items'][] = [
|
||||
'guid' => $row['guid'],
|
||||
'title' => $row['title'],
|
||||
'description' => $row['description'],
|
||||
'link' => $row['link'],
|
||||
'created_at' => Carbon::parse($row['pubDate'])->addHours(2)->format('Y-m-d H:i:s'),
|
||||
];
|
||||
}
|
||||
|
||||
$return['previous_page'] = $page > 1 ? $page-- : false;
|
||||
$return['next_page'] = count( $rows['channel']['item'] ) === 24 ? ++$page : false;
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Mathiaspalmqvist\LaravelCisionFeed;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class LaravelCisionFeedProvider extends ServiceProvider
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Mattitja\Cision\Support;
|
||||
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use DOMText;
|
||||
|
||||
class HtmlCleaner
|
||||
{
|
||||
/**
|
||||
* Renders a DOM node into safe HTML, preserving only allowed tags.
|
||||
*/
|
||||
public static function renderNode(DOMNode $node, array $allowedTags, array $allowedInline, string $parentTag = ''): ?string
|
||||
{
|
||||
if ($node instanceof DOMText) {
|
||||
return $node->wholeText;
|
||||
}
|
||||
|
||||
if (! ($node instanceof DOMElement)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag = $node->nodeName;
|
||||
|
||||
if ($tag === 'br') {
|
||||
if ($parentTag === 'p') {
|
||||
$prev = $node->previousSibling;
|
||||
$next = $node->nextSibling;
|
||||
|
||||
$hasTextBefore = $prev instanceof DOMText && trim($prev->textContent) !== '';
|
||||
$hasTextAfter = $next instanceof DOMText && trim($next->textContent) !== '';
|
||||
|
||||
if ($hasTextBefore && $hasTextAfter) {
|
||||
return '<br>';
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$inner = '';
|
||||
|
||||
foreach ($node->childNodes as $child) {
|
||||
$rendered = self::renderNode($child, $allowedTags, $allowedInline, $tag);
|
||||
if ($rendered !== null) {
|
||||
$inner .= $rendered;
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($tag, $allowedTags)) {
|
||||
if ($tag === 'p') {
|
||||
$html = $node->C14N();
|
||||
$isItalic = str_starts_with(trim($html), '<em') || str_starts_with(trim($html), '<i');
|
||||
$class = $isItalic ? ' class="italic"' : '';
|
||||
|
||||
return "<p{$class}>".trim($inner).'</p>';
|
||||
}
|
||||
|
||||
return "<{$tag}>".trim($inner)."</{$tag}>";
|
||||
}
|
||||
|
||||
if (in_array($tag, $allowedInline)) {
|
||||
return "<{$tag}>{$inner}</{$tag}>";
|
||||
}
|
||||
|
||||
return $inner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans a raw HTML string: removes unwanted content, empty tags, and excess whitespace.
|
||||
*/
|
||||
public static function clean(string $html): string
|
||||
{
|
||||
$html = preg_replace('/<h1\b[^>]*>.*?<\/h1>/i', '', $html, 1);
|
||||
$html = preg_replace('/<(\w+)[^>]*>\s*<\/\1>/', '', $html);
|
||||
|
||||
$unwantedStrings = [
|
||||
'Report this content',
|
||||
'<p><strong>Taggar:</strong></p>',
|
||||
];
|
||||
|
||||
foreach ($unwantedStrings as $str) {
|
||||
$html = str_replace($str, '', $html);
|
||||
}
|
||||
|
||||
$html = preg_replace('/\b[a-z]{3},\s[a-z]{3}\s\d{2},\s\d{4}\s\d{2}:\d{2}\sCET\b/i', '', $html);
|
||||
$html = str_replace(["\u{A0}", "\t", "\r"], ' ', $html);
|
||||
$html = preg_replace('/[ ]{2,}/', ' ', $html);
|
||||
$html = preg_replace("/(\n\s*){2,}/", "\n", $html);
|
||||
$html = str_replace([' ', ' \n'], '', $html);
|
||||
|
||||
return trim($html);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
arch('it will not use debugging functions')
|
||||
->expect(['dd', 'dump', 'ray'])
|
||||
->each->not->toBeUsed();
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Mattitja\Cision\Cision;
|
||||
|
||||
it('can fetch press releases from the RSS feed', function () {
|
||||
Http::fake([
|
||||
'news.cision.com/*' => Http::response(file_get_contents(__DIR__.'/Fixtures/rss-press.xml')),
|
||||
]);
|
||||
|
||||
$cision = new Cision('test-company');
|
||||
$data = $cision->press();
|
||||
|
||||
expect($data)->toBeArray();
|
||||
expect($data)->toHaveKey('items');
|
||||
expect($data['items'])->not->toBeEmpty();
|
||||
|
||||
$item = $data['items'][0];
|
||||
expect($item)->toHaveKeys(['guid', 'title', 'description', 'link', 'created_at']);
|
||||
});
|
||||
|
||||
it('can fetch and parse full article content', function () {
|
||||
Http::fake([
|
||||
'news.cision.com/*' => Http::response(file_get_contents(__DIR__.'/Fixtures/article.html')),
|
||||
]);
|
||||
|
||||
$cision = new Cision;
|
||||
$content = $cision->fetchContent('https://news.cision.com/se/example/r/test,c123456');
|
||||
|
||||
expect($content)->toHaveKeys(['header', 'published_at', 'content']);
|
||||
expect($content['header'])->not->toBeEmpty();
|
||||
expect($content['content'])->toContain('<p');
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
<article>
|
||||
<div>
|
||||
<h1>Test Article Header</h1>
|
||||
<time pubdate="2025-07-04 07:55:00Z" datetime="2025-07-04 07:55:00Z">fre, jul 04, 2025 09:55 CET</time>
|
||||
<p><strong>Lead:</strong> This is a test article paragraph.</p>
|
||||
<p><em>Italic sentence.</em></p>
|
||||
<ul>
|
||||
<li><strong>Key:</strong> Value</li>
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>Test Press Releases</title>
|
||||
<item>
|
||||
<guid>test-guid</guid>
|
||||
<title>Test Press Title</title>
|
||||
<description>This is a test press release description.</description>
|
||||
<link>https://news.cision.com/se/example/r/test,c123456</link>
|
||||
<pubDate>Fri, 04 Jul 2025 07:55:00 GMT</pubDate>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Mattitja\Cision\Tests\TestCase;
|
||||
|
||||
uses(TestCase::class)->in(__DIR__);
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Mattitja\Cision\Tests;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Mattitja\Cision\CisionServiceProvider;
|
||||
use Orchestra\Testbench\TestCase as Orchestra;
|
||||
|
||||
class TestCase extends Orchestra
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Factory::guessFactoryNamesUsing(
|
||||
fn (string $modelName) => 'Mattitja\\Cision\\Database\\Factories\\'.class_basename($modelName).'Factory'
|
||||
);
|
||||
}
|
||||
|
||||
protected function getPackageProviders($app)
|
||||
{
|
||||
return [
|
||||
CisionServiceProvider::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function getEnvironmentSetUp($app)
|
||||
{
|
||||
config()->set('database.default', 'testing');
|
||||
|
||||
/*
|
||||
foreach (\Illuminate\Support\Facades\File::allFiles(__DIR__ . '/database/migrations') as $migration) {
|
||||
(include $migration->getRealPath())->up();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInita551a78ff0c5533943a66fc25192491f::getLoader();
|
||||
Vendored
-572
@@ -1,572 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var ?string */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<int, string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<string, string[]>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var bool[]
|
||||
* @psalm-var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var ?string */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var self[]
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param ?string $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, array<int, string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] Array of classname => path
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $classMap Class to filename map
|
||||
* @psalm-param array<string, string> $classMap
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param string[]|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param string[]|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param string[]|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param string[]|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
||||
*
|
||||
* @return self[]
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
* @private
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
);
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Mathiaspalmqvist\\LaravelCisionFeed\\' => array($baseDir . '/src'),
|
||||
);
|
||||
Vendored
-55
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInita551a78ff0c5533943a66fc25192491f
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInita551a78ff0c5533943a66fc25192491f', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInita551a78ff0c5533943a66fc25192491f', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInita551a78ff0c5533943a66fc25192491f::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
Vendored
-36
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInita551a78ff0c5533943a66fc25192491f
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'M' =>
|
||||
array (
|
||||
'Mathiaspalmqvist\\LaravelCisionFeed\\' => 35,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Mathiaspalmqvist\\LaravelCisionFeed\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInita551a78ff0c5533943a66fc25192491f::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInita551a78ff0c5533943a66fc25192491f::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInita551a78ff0c5533943a66fc25192491f::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user