{"id":18552037,"url":"https://github.com/baraja-core/wordpress-post-feed","last_synced_at":"2026-01-07T08:02:08.674Z","repository":{"id":48560963,"uuid":"308281562","full_name":"baraja-core/wordpress-post-feed","owner":"baraja-core","description":"Simple PHP API service for download and list of new blog posts from Wordpress RSS.","archived":false,"fork":false,"pushed_at":"2024-06-09T20:21:01.000Z","size":56,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-05T09:38:48.900Z","etag":null,"topics":["api","articles","feed","php","rss","wordpress","wordpress-rss","wp"],"latest_commit_sha":null,"homepage":"https://php.baraja.cz","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/baraja-core.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/funding.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"janbarasek","custom":["https://brj.app","https://baraja.cz","https://php.baraja.cz"]}},"created_at":"2020-10-29T09:41:07.000Z","updated_at":"2024-06-09T20:13:48.000Z","dependencies_parsed_at":"2024-06-09T21:34:56.040Z","dependency_job_id":"e761571d-f4df-4695-8a83-26beb98e9846","html_url":"https://github.com/baraja-core/wordpress-post-feed","commit_stats":{"total_commits":60,"total_committers":2,"mean_commits":30.0,"dds":0.01666666666666672,"last_synced_commit":"cd07dcf7178d4943530ba4c439a04e4816ebd884"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fwordpress-post-feed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fwordpress-post-feed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fwordpress-post-feed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baraja-core%2Fwordpress-post-feed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/baraja-core","download_url":"https://codeload.github.com/baraja-core/wordpress-post-feed/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246049632,"owners_count":20715511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","articles","feed","php","rss","wordpress","wordpress-rss","wp"],"created_at":"2024-11-06T21:11:48.360Z","updated_at":"2026-01-07T08:02:08.662Z","avatar_url":"https://github.com/baraja-core.png","language":"PHP","funding_links":["https://github.com/sponsors/janbarasek","https://brj.app","https://baraja.cz","https://php.baraja.cz"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align='center'\u003e\n  \u003cpicture\u003e\n    \u003csource media='(prefers-color-scheme: dark)' srcset='https://cdn.brj.app/images/brj-logo/logo-regular.png'\u003e\n    \u003cimg src='https://cdn.brj.app/images/brj-logo/logo-dark.png' alt='BRJ logo'\u003e\n  \u003c/picture\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://brj.app\"\u003eBRJ organisation\u003c/a\u003e\n\u003c/div\u003e\n\u003chr\u003e\n\n# WordPress Post Feed\n\nA lightweight PHP library for fetching, parsing, and caching WordPress RSS feeds with automatic image handling.\n\nThe library provides a simple API to download blog posts from any WordPress RSS feed, automatically caches the content to minimize network requests, and handles image downloading and local storage for improved performance and reliability.\n\n## :sparkles: Key Features\n\n- **Automatic RSS feed parsing** - Extracts posts with title, description, link, date, author, and categories\n- **Smart caching system** - Feed content is cached with configurable expiration time (default: 2 hours)\n- **Image management** - Automatically downloads and stores post images locally\n- **Pagination support** - Built-in `limit` and `offset` parameters for easy pagination\n- **Zero-config usage** - Works out of the box without any dependencies when used standalone\n- **Nette Framework integration** - Full DI container support with configurable extension\n- **Security-first image handling** - Validates image types before saving to prevent malicious files\n\n## :building_construction: Architecture Overview\n\nThe library consists of four main components that work together to provide a complete feed management solution:\n\n```\n┌─────────────────────────────────────────────────────────────────────┐\n│                        WordPress RSS Feed                           │\n│                    (External WordPress Blog)                        │\n└───────────────────────────────┬─────────────────────────────────────┘\n                                │\n                                ▼\n┌─────────────────────────────────────────────────────────────────────┐\n│                           Feed Service                              │\n│  ┌─────────────────────────────────────────────────────────────┐   │\n│  │  • Downloads RSS feed via cURL                              │   │\n│  │  • Parses XML using DOMDocument                             │   │\n│  │  • Extracts post data (title, description, link, date...)   │   │\n│  │  • Manages cache read/write operations                      │   │\n│  │  • Extracts images from post descriptions                   │   │\n│  └─────────────────────────────────────────────────────────────┘   │\n└───────────────────────────────┬─────────────────────────────────────┘\n                                │\n                ┌───────────────┴───────────────┐\n                ▼                               ▼\n┌───────────────────────────────┐ ┌───────────────────────────────────┐\n│         Nette Cache           │ │         ImageStorage              │\n│  ┌─────────────────────────┐  │ │  ┌─────────────────────────────┐  │\n│  │ • FileStorage (default) │  │ │  │ • Downloads images via cURL │  │\n│  │ • Configurable TTL      │  │ │  │ • Validates image types     │  │\n│  │ • Auto-invalidation     │  │ │  │ • Stores to local disk      │  │\n│  └─────────────────────────┘  │ │  │ • Generates URLs            │  │\n└───────────────────────────────┘ │  └─────────────────────────────┘  │\n                                  └───────────────────────────────────┘\n                                                  │\n                                                  ▼\n                                  ┌───────────────────────────────────┐\n                                  │           Post Entity             │\n                                  │  ┌─────────────────────────────┐  │\n                                  │  │ • title (string)            │  │\n                                  │  │ • description (string)      │  │\n                                  │  │ • link (string)             │  │\n                                  │  │ • date (DateTimeImmutable)  │  │\n                                  │  │ • creator (string|null)     │  │\n                                  │  │ • categories (array)        │  │\n                                  │  │ • mainImageUrl (string|null)│  │\n                                  │  └─────────────────────────────┘  │\n                                  └───────────────────────────────────┘\n```\n\n### :package: Components\n\n| Component | Description |\n|-----------|-------------|\n| `Feed` | Main service responsible for downloading, parsing, and caching RSS feeds. Orchestrates the entire feed retrieval process. |\n| `Post` | Data entity representing a single blog post with all its properties and image URL helpers. |\n| `ImageStorage` | Service for downloading, validating, and storing images locally. Provides URL generation for stored images. |\n| `WordpressPostFeedExtension` | Nette DI extension for seamless framework integration with full configuration support. |\n\n## :package: Installation\n\nIt's best to use [Composer](https://getcomposer.org) for installation, and you can also find the package on\n[Packagist](https://packagist.org/packages/baraja-core/wordpress-post-feed) and\n[GitHub](https://github.com/baraja-core/wordpress-post-feed).\n\nTo install, simply use the command:\n\n```shell\n$ composer require baraja-core/wordpress-post-feed\n```\n\nYou can use the package manually by creating an instance of the internal classes, or register a DIC extension to link the services directly to the Nette Framework.\n\n### Requirements\n\n- PHP 8.0 or higher\n- ext-curl extension\n- Nette Caching component (installed automatically)\n\n## :gear: Configuration\n\n### Standalone Usage\n\nThe library works out of the box without any configuration:\n\n```php\n$feed = new \\Baraja\\WordPressPostFeed\\Feed;\n```\n\nBy default, the `Feed` service will:\n- Create a temporary directory for caching in the system temp folder\n- Set cache expiration to 2 hours\n- Create an `ImageStorage` instance with default settings\n\n### Nette Framework Integration\n\nIf you are installing the package into the Nette Framework, register the extension in your configuration:\n\n```yaml\nextensions:\n    wordpressPostFeed: Baraja\\WordPressPostFeed\\WordpressPostFeedExtension\n```\n\n### Advanced Configuration\n\nThe extension supports the following configuration options:\n\n```yaml\nwordpressPostFeed:\n    # Cache expiration time (supports human-readable format)\n    expirationTime: '2 hours'\n\n    # Absolute path for image storage\n    imageStoragePath: '%wwwDir%/assets/blog-images'\n\n    # Relative path used in URLs (required when imageStoragePath is set)\n    imageRelativeStoragePath: 'assets/blog-images'\n```\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `expirationTime` | string | `'2 hours'` | Cache expiration time in human-readable format |\n| `imageStoragePath` | string | `null` | Absolute filesystem path for storing images |\n| `imageRelativeStoragePath` | string | `'wordpress-post-feed'` | Relative URL path for accessing stored images |\n\n## :rocket: Basic Usage\n\n### Loading Posts from a Feed\n\n```php\n$feed = new \\Baraja\\WordPressPostFeed\\Feed;\n\n$posts = $feed-\u003eload('https://blog.example.com/feed/');\n\nforeach ($posts as $post) {\n    echo $post-\u003egetTitle();\n    echo $post-\u003egetDescription();\n    echo $post-\u003egetLink();\n    echo $post-\u003egetDate()-\u003eformat('Y-m-d');\n    echo $post-\u003egetCreator();\n    echo json_encode($post-\u003egetCategories());\n    echo $post-\u003egetMainImageUrl();\n}\n```\n\nThe feed will be downloaded automatically, then cached and further requests will be handled instantly straight from the cache.\n\n### Pagination with Limit and Offset\n\nIf you only need to retrieve some posts from the feed, use the `limit` and `offset` arguments:\n\n```php\n$posts = $feed-\u003eload(\n    url: 'https://blog.example.com/feed/',\n    limit: 3,\n    offset: 1,\n);\n```\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `url` | string | required | The URL of the WordPress RSS feed |\n| `limit` | int\\|null | `null` | Maximum number of posts to return (null = all) |\n| `offset` | int | `0` | Number of posts to skip from the beginning |\n\n### Post Entity Methods\n\nThe `Post` entity provides the following methods:\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `getTitle()` | `string` | Returns the post title (HTML tags stripped) |\n| `getDescription()` | `string` | Returns the post description/excerpt |\n| `getLink()` | `string` | Returns the permalink to the original post |\n| `getDate()` | `DateTimeImmutable` | Returns the publication date |\n| `getCreator()` | `string\\|null` | Returns the author name |\n| `getCategories()` | `array\u003cint, string\u003e` | Returns an array of category names |\n| `getMainImageUrl()` | `string\\|null` | Returns the original external image URL |\n| `getAbsoluteInternalUrl()` | `string\\|null` | Returns the absolute URL to locally stored image |\n| `getRelativeInternalUrl()` | `string\\|null` | Returns the relative URL to locally stored image |\n\n## :floppy_disk: Caching System\n\n### How Caching Works\n\nPosts are automatically cached for the set period (default 2 hours). The caching system operates on two levels:\n\n1. **Raw feed caching** - The downloaded XML feed is cached to avoid repeated HTTP requests\n2. **Parsed post caching** - The parsed `Post` objects are cached separately for each limit/offset combination\n\nWhen the cache expires, the next request will:\n1. Download the fresh feed\n2. Parse all posts\n3. Store images locally\n4. Update the cache\n\n### Cache Management\n\n#### Manual Cache Update\n\nIf you want to refresh the cache before expiration (e.g., via cron job):\n\n```php\n$feed = new \\Baraja\\WordPressPostFeed\\Feed;\n$feed-\u003eupdateCache('https://blog.example.com/feed/');\n```\n\nThis is useful for ensuring all requests are served from cache instantly, even after expiration.\n\n#### Clear All Cache\n\nTo completely clear the feed cache:\n\n```php\n$feed = new \\Baraja\\WordPressPostFeed\\Feed;\n$feed-\u003eclearCache();\n```\n\n### Recommended Cron Setup\n\nFor production environments, it's recommended to set up a cron job that refreshes the cache periodically:\n\n```php\n// cron.php - Run every hour\n$feed = new \\Baraja\\WordPressPostFeed\\Feed;\n\n$feedUrls = [\n    'https://blog.example.com/feed/',\n    'https://another-blog.com/feed/',\n];\n\nforeach ($feedUrls as $url) {\n    $feed-\u003eupdateCache($url);\n}\n```\n\nThis ensures that user requests are always served from cache and never have to wait for feed downloads.\n\n## :framed_picture: Image Manipulation\n\nWhen downloading posts from the feed, images are automatically downloaded and copied to disk. The image storage is provided by the `\\Baraja\\WordPressPostFeed\\ImageStorage` service, which is automatically registered by the library.\n\n### Default Storage Location\n\nThe default directory for storing images is `wordpress-post-feed` relative to your `index.php` (web root).\n\n### ImageStorage Service Methods\n\nThe service provides the following methods:\n\n| Method | Description |\n|--------|-------------|\n| `save(string $url): void` | Downloads the image from the URL and saves it to disk |\n| `getInternalPath(string $url): string` | Returns the absolute filesystem path for the image |\n| `getAbsoluteInternalUrl(string $url): string` | Returns the absolute URL to retrieve the image |\n| `getRelativeInternalUrl(string $url): string` | Returns the relative URL to retrieve the image |\n\n### Working with Images from Posts\n\nWorking with images is also available directly from the `Post` entity:\n\n```php\nforeach ($posts as $post) {\n    // Original external URL (from WordPress)\n    $externalUrl = $post-\u003egetMainImageUrl();\n\n    // Local absolute URL (your domain)\n    $absoluteUrl = $post-\u003egetAbsoluteInternalUrl();\n\n    // Local relative URL (for use in templates)\n    $relativeUrl = $post-\u003egetRelativeInternalUrl();\n}\n```\n\n### Supported Image Formats\n\nThe library validates downloaded images and supports the following formats:\n\n| Format | Constant |\n|--------|----------|\n| JPEG | `ImageStorage::JPEG` |\n| PNG | `ImageStorage::PNG` |\n| GIF | `ImageStorage::GIF` |\n| WebP | `ImageStorage::WEBP` |\n| BMP | `ImageStorage::BMP` |\n\nIf a downloaded file is not a valid image of these types, a `RuntimeException` is thrown to prevent potential security issues.\n\n### Image Naming Strategy\n\nImages are stored with a deterministic naming scheme to prevent collisions and ensure readability:\n\n1. A 7-character MD5 hash prefix is generated from the URL\n2. The original filename is sanitized (webalized) and truncated to 64 characters\n3. Files are organized into subdirectories based on WordPress date structure (YYYY-MM) or URL hash\n\nExample: `https://blog.example.com/wp-content/uploads/2024/03/my-image.jpg` becomes:\n```\nwordpress-post-feed/2024-03/a1b2c3d-my-image.jpg\n```\n\n## :hammer_and_wrench: Advanced Usage\n\n### Custom Cache Storage\n\nYou can provide your own cache storage implementation:\n\n```php\nuse Nette\\Caching\\Storages\\MemcachedStorage;\n\n$memcached = new Memcached;\n$memcached-\u003eaddServer('localhost', 11211);\n\n$storage = new MemcachedStorage($memcached);\n$feed = new \\Baraja\\WordPressPostFeed\\Feed(storage: $storage);\n```\n\n### Custom Image Storage Location\n\n```php\n$imageStorage = new \\Baraja\\WordPressPostFeed\\ImageStorage(\n    storagePath: '/var/www/html/assets/blog-images',\n    relativeStoragePath: 'assets/blog-images',\n);\n\n$feed = new \\Baraja\\WordPressPostFeed\\Feed(imageStorage: $imageStorage);\n```\n\n### Custom Expiration Time\n\n```php\n$feed = new \\Baraja\\WordPressPostFeed\\Feed(\n    expirationTime: '30 minutes',\n);\n```\n\nSupported time formats include: `'1 hour'`, `'30 minutes'`, `'2 days'`, etc.\n\n### Dependency Injection in Nette\n\nWhen using the Nette Framework, you can inject the services directly:\n\n```php\nfinal class BlogPresenter extends Nette\\Application\\UI\\Presenter\n{\n    public function __construct(\n        private \\Baraja\\WordPressPostFeed\\Feed $feed,\n    ) {\n    }\n\n    public function renderDefault(): void\n    {\n        $this-\u003etemplate-\u003eposts = $this-\u003efeed-\u003eload(\n            url: 'https://blog.example.com/feed/',\n            limit: 5,\n        );\n    }\n}\n```\n\n## :warning: Error Handling\n\nThe library handles errors gracefully:\n\n- **Empty feed response** - Throws `RuntimeException` with descriptive message\n- **Invalid image format** - Throws `RuntimeException` to prevent security issues\n- **Broken image URLs** - Triggers a warning but continues processing\n- **Invalid feed URLs** - Triggers a warning but continues processing\n- **Missing absolute URL in CLI** - Throws `LogicException` with guidance\n\nExample of handling errors:\n\n```php\ntry {\n    $posts = $feed-\u003eload('https://blog.example.com/feed/');\n} catch (\\RuntimeException $e) {\n    // Handle feed loading errors\n    error_log('Feed error: ' . $e-\u003egetMessage());\n}\n```\n\n## :bust_in_silhouette: Author\n\n**Jan Barasek**\n\n- Website: [https://baraja.cz](https://baraja.cz)\n- GitHub: [@janbarasek](https://github.com/janbarasek)\n\n## :page_facing_up: License\n\n`baraja-core/wordpress-post-feed` is licensed under the MIT license. See the [LICENSE](https://github.com/baraja-core/wordpress-post-feed/blob/master/LICENSE) file for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaraja-core%2Fwordpress-post-feed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbaraja-core%2Fwordpress-post-feed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaraja-core%2Fwordpress-post-feed/lists"}