diff --git a/public/img/appdata.png b/public/img/appdata.png new file mode 100644 index 000000000..4d9deb007 Binary files /dev/null and b/public/img/appdata.png differ diff --git a/resources/views/docs/1/digging-deeper/_index.md b/resources/views/docs/1/digging-deeper/_index.md new file mode 100644 index 000000000..7d67168df --- /dev/null +++ b/resources/views/docs/1/digging-deeper/_index.md @@ -0,0 +1,4 @@ +--- +title: Digging Deeper +order: 3 +--- diff --git a/resources/views/docs/1/digging-deeper/broadcasting.md b/resources/views/docs/1/digging-deeper/broadcasting.md new file mode 100644 index 000000000..b44330eed --- /dev/null +++ b/resources/views/docs/1/digging-deeper/broadcasting.md @@ -0,0 +1,41 @@ +--- +title: Broadcasting +order: 100 +--- + +# Broadcasting + +NativePHP fires various events during its operations. You may listen for these events using any event listener in your +application as you normally would. + +NativePHP also broadcasts these events over a websocket connection to the `nativephp` broadcast channel. This allows +you to listen for these events in real-time using Laravel Echo and react to these events on your application's +front-end. + +A full list of all events fired and broadcast by NativePHP can be found in the +[src/Events/](https://github.com/nativephp/laravel/tree/main/src/Events) folder. + +## Broadcasting custom events + +You can also broadcast your own custom events. Simply instruct your event to implement the `ShouldBroadcast` contract +and define the `broadcastsOn` method in your event, returning `nativephp` as one of the channels it broadcasts to: + +```php +use Illuminate\Broadcasting\Channel; +use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; + +class JobFinished implements ShouldBroadcastNow +{ + public function broadcastOn(): array + { + return [ + new Channel('nativephp'), + ]; + } +} +``` + +This is great for times when you want to offload an intensive task to a background queue and await its completion +without constantly polling your application for its status. + +Your fired event will be broadcast and your application can listen for it and just as your normally would. diff --git a/resources/views/docs/1/digging-deeper/databases.md b/resources/views/docs/1/digging-deeper/databases.md new file mode 100644 index 000000000..c95aae447 --- /dev/null +++ b/resources/views/docs/1/digging-deeper/databases.md @@ -0,0 +1,59 @@ +--- +title: Databases +order: 200 +--- + +# Working with Databases + +Almost every application needs a database, especially if your app is working with complex user data or communicating +with an API. A database is an efficient and reliable way to persist structured data across multiple versions of +your application. + +When building a _server-side_ application, you are free to choose the database engine you prefer. But in the context of +a self-contained native application, your choices are limited to: +- what you can reasonably bundle with your app; or +- what you can expect the user's system to have installed. + +**To keep the footprint of your application small, NativePHP currently only supports SQLite out of the box.** + +You can interact with SQLite via PDO or an ORM, such as Eloquent, in exactly the way you're used to. + +## SQLite + +[SQLite](https://sqlite.org/) is a feature-rich, portable, lightweight, file-based database. It's perfect for native +applications that need persistent storage of complex data structures with the speed and tooling of SQL. + +Its small footprint and minimal dependencies make it ideal for cross-platform, native applications. Your users +don't need to install anything else besides your app, and it doesn't add hundreds of MBs to your bundle, +keeping download & install size small. + +### Configuration + +You do not need to do anything special to configure your application to use SQLite. NativePHP will automatically: +- switch to using SQLite when building your application, +- create a database file for you in the `storage` directory on the user's system, +- configure your application to use that database file, and +- run your migrations each time your app starts. + +## Migrations + +When writing migrations, you need to consider any special recommendations for working with SQLite. For example, +SQLite disables foreign key constraints by default. If your application relies upon foreign key constraints, +[you need to enable SQLite support for them](https://laravel.com/docs/10.x/database#configuration) before +writing your migrations. + +**It's important to test your migrations before releasing updates!** You don't want to accidentally delete your user's +data when they update your app. + +## When not to use a database + +If you're only storing small amounts of very simple metadata or working files, you may not need a database at all. +Consider storing files instead. These could be JSON, CSV, plain text or any other format that makes sense for +your application. + +Consider also using file storage for very critical metadata about the state of your application on a user's device. +If you rely on the same database you store the user's data to store this information, if the database becomes +corrupted for any reason, your application may not be able to start at all. + +If you store this information in a file, you can at least instruct your users to delete the file and restart the +application lowering the risk of deleting their data. diff --git a/resources/views/docs/1/digging-deeper/files.md b/resources/views/docs/1/digging-deeper/files.md new file mode 100644 index 000000000..f52e00edc --- /dev/null +++ b/resources/views/docs/1/digging-deeper/files.md @@ -0,0 +1,84 @@ +--- +title: Files +order: 300 +--- + +# Files & Paths + +Working with files in NativePHP is just like working with files in a regular Laravel application. To achieve this, +NativePHP rewrites the `Application::$storagePath()` (and thus `app()->storagePath()` and the `storage_path()` helper) +to the [Electron `app.getPath('appData')` path](https://www.electronjs.org/docs/latest/api/app#appgetpathname), +which is different for each operating system. + +This means that you can continue to use Laravel's `Storage` facade to store and retrieve files on your user's file +system just as you would on your server. + +If you use the default Storage configuration for the `local` filesystem, your `local` disk will also point to this +`appdata` directory, followed by `storage/app`. + +![](/img/appdata.png) + +Here you may see some folders you recognise, namely `database` and `storage`. The other folders are managed by Electron. +The `storage` folder is exactly the same `storage` directory you are used to seeing in your Laravel application. It +stores various caches and also application logs. + +You should use this `Application::storagePath()` when storing files on your user's computer that need to remain +available even when your application is updated or removed from the system, e.g. your application's configuration, +settings and any user data that the user doesn't need direct access to. + +It's also the location where your SQLite database will be stored. + +## Storing files elsewhere + +NativePHP doesn't interfere with any of your _existing_ filesystem configuration, so you may continue to configure +[Filesystems](https://laravel.com/docs/filesystems) as you normally would, however you should be aware that it does +_add_ some new default filesystems for your convenience. + +Consider that your users want to store their files in locations other than the obscure `appdata` directories on their +preferred OS. To that end, NativePHP provides a variety of convenient `filesystems` which are configured at runtime to +point to the respective, platform-specific directories for the current user. + +[warning] +If your application also defines any of these filesystems, NativePHP will override their configuration with its own. +[/warning] + +You can use these filesystem simply using the `Storage` facade like this: + +```php +Storage::disk('user.home')->get('file.txt'); +Storage::disk('user.desktop')->get('file.txt'); +Storage::disk('user.documents')->get('file.txt'); +Storage::disk('user.downloads')->get('file.txt'); +Storage::disk('user.music')->get('file.txt'); +Storage::disk('user.pictures')->get('file.txt'); +Storage::disk('user.videos')->get('file.txt'); +Storage::disk('user.recent')->get('file.txt'); +``` + +Note that the PHP process which runs your application operates with the same privileges as the logged-in user, this +means your application is able to read and write files wherever your user is authorised to. + +Generally, you should only read and write files to the user's `home` directory or your app's `appdata` directory. Be +aware that some operating systems now actively prompt the user to grant permissions to apps when they first attempt to +access directories in the user's home directory. + +See [Security](/docs/digging-deeper/security) for more considerations. + +[aside] +You can also continue to use cloud storage providers if you wish. + +However, be mindful that an application installed on a user's device is even more likely to experience network +disruption than one operating on a server in the cloud, as your users may be without an internet connection at any +time. + +You should prepare more carefully for such scenarios when interacting with any APIs that require network connectivity +by checking for a connection _before_ making a request and/or handling exceptions gracefully should a request fail. + +This will help maintain a smooth user experience +[/aside] + +NativePHP uses the `local` disk by default. If you would like to use a different disk, you may configure this in your +`config/filesystems.php` file. + +Remember, you can set the filesystem disk your application uses by default in your `config/filesystems.php` file or by +adding a `FILESYSTEM_DISK` variable to your `.env` file. diff --git a/resources/views/docs/1/digging-deeper/security.md b/resources/views/docs/1/digging-deeper/security.md new file mode 100644 index 000000000..4a0e6e7a0 --- /dev/null +++ b/resources/views/docs/1/digging-deeper/security.md @@ -0,0 +1,155 @@ +--- +title: Security +order: 400 +--- +# Security + +When building desktop applications it's essential to take your application's security to the next level, both to +protect your application and infrastructure, but also to protect your users, their system and their data. This is a +complex and wide-reaching topic. Please take time to thoroughly understand everything discussed in this chapter. + +Remember that we can't cover everything here either, so please use good judgement when implementing features of your +application that allows users to manipulate data on their filesystem or other sources. + +## Protecting your application and infrastructure + +A major consideration for NativePHP is how it can protect _your_ application. + +### Secrets and .env +As your application is being installed on systems outside of your/your organisation's control, it is important to think +of the environment that it's in as _potentially_ hostile, which is to say that any secrets, passwords or keys are +could fall into the hands of someone who might try to abuse them. + +This means you should, where possible, use unique keys for each installation, preferring to generate these at first-run +or on every run rather than sharing the same key for every user across many installations. + +Especially if your application is communicating with any private APIs over the network, we highly recommend that your +application and any API use a robust and secure authentication protocol, such as OAuth2, that enables you to create and +distribute unique and expiring tokens (an expiration date less than 48 hours in the future is recommended) with a high +level of entropy, as this makes them hard to guess and hard to abuse. + +[hypothetical] +When your application runs for the first time on a user's device, NativePHP generates a new `APP_KEY`. This means that +when your application uses encryption features, by default each user's encryption key will be different. This means +that an attacker won't be able to acquire an `APP_KEY` from one user to decrypt content encrypted by another user. + +This also presents a challenge for you if you wish to centralise or backup any user data by sending it to the cloud. +If your wider infrastructure relies on having access to unencrypted data, make sure you are clear with your users about +what data you are collecting and how it is used. + +Depending on the laws applicable where you live or intend to distribute your software, you may have to write detailed +privacy policies which your users must agree to before they can use your application. In some cases, you may not be +allowed to use encryption features. + +NativePHP leaves it up to you to determine your obligations in this regard. If you are unsure, please seek appropriate +legal advice. +[/hypothetical] + +If your application allows users to connect _their own_ API keys for a service, you should treat these keys with great +care. If you choose to store them anywhere (either in a [File](/docs/digging-deeper/files) or +[Database](/docs/digging-deeper/databases)), make sure you store them encrypted and decrypt them only when in use. + +### Files and privileges + +Your application runs in a privileged state thanks to the PHP runtime being executed as the user who is currently +operating the system. This is convenient, but it also comes with risks. Your application has access to everything that +the user is authorized to access on the system. + +You should limit where you are reading and writing files to the locations your user expects. These are the `appdata` +folder for the combination of your application and this user and the user's `home` directory (and the other user +subdirectories). + +All of these can be done simply by using the provided Storage filesystems detailed in +[Files](/docs/digging-deeper/files). + +### The web servers + +NativePHP works by spinning up web servers on each side of the runtime environment: one on the PHP side to execute your +application and another on the Electron side, to interact with Electron's native environment hooks for the operating +system. It then bridges the gap between the two by making _authenticated and encrypted_ HTTP calls between the two +using a pre-shared, dynamic key that is regenerated every time your application starts. + +This prevents any third-party software from snooping/sniffing the connection and 'tinkering' with either your +application or the Electron environment. This means that your application's front-end will only be accessible through +your Electron application shell and the Electron APIs will only respond to your application. + +**You MUST NOT bypass this security measure!** If you do, your application will be open to attack from very basic HTTP +calls, which it is trivial for any installed application to make, or even for your user to be coerced into making via a +web browser (e.g. from a phishing attack). + +By default, Laravel's built-in CSRF and CORS protections will go some way to preventing many of these kinds of attacks +but you should do all you can to prevent unwanted attack vectors from being made available. + +## Protecting your users and their data + +Equally important is how your app protects users. NativePHP is a complex combination of powerful software and so there +are a number of risks associated with its use. + +### When sending data over the network + +**Always use HTTPS to communicate with web services.** This ensures that any data sent between your user's device and +the service is encrypted in transit. + +### The PHP executable + +Currently, the bundled PHP executable can be used by any user or application that knows where to find it and has +privileges to execute binaries in that location. + +This is a potential attack vector that your users ought to be aware of when they are installing other applications. If +a user installs an application that they don't trust, it may attempt to use the PHP binary bundled with your application +to execute arbitrary code on your user's device. This is known as a Remote Code Execution attack (or RCE). + +While this may not directly affect your application (unless it's the target of such an attack), you can still help users +to secure their device by reminding them of their responsibility to only install trusted software from reputable +vendors. + +There's very little that can be done to mitigate this kind of attack in practice, just the same as any application you +install now on your device could use any other application installed. + +### Interpreted code + +[hypothetical] + +As your application is just a well-organized bundle of plain-text PHP scripts which is not compiled to machine-code +until runtime, it is trivial for anyone to change the execution of your application by diving into one of these files +and altering the code. + +The approach we've taken to mitigate this is to verify that the code is as expected using a signature of all the files. +This signature is computed every time your application is booted, but by default it is done in the background so it +doesn't noticeably slow down your application's boot sequence, which could result in a poor user experience. + +Once complete, this signature is then compared to the signature that Electron expects to see for your application, which +is computed and stored in your application bundle at build time. If the signatures match, the user will not notice +anything, however if they do not match, Electron will interrupt the user with a warning to indicate that the application +has been tampered with and may not be safe to use. + +This warning can be dismissed and the user may continue, but it will re-appear again when they quit your app and +re-open it. + +If your application is not particularly large, you may choose to have this signature calculation run to completion +_before_ your application is booted to prevent any alterations from executing as your app boots up. This is, of course, +safer for your user, but initially slower each time your app boots up. However, you could use a splash screen and a +progress bar to give your user some visual feedback while this is happening. + +In future versions of NativePHP, we hope to be able to offer a more robust solution to this problem. + +#### How it works + +When you run `php artisan native:build`, we perform the following hashing function: + +We take an `md5` hash of each static file in your application - not just PHP files! - and this includes all of your +Composer dependencies too. We then compute an `md5` hash of each of these hashes. + +We store the final computed hash as a hardcoded value inside the Electron bundle. This is a signature or checksum of +your entire codebase which can be verified at runtime when a user opens your application. + +We run the exact same hashing mechanic on each run of your application in a background thread. Once complete, the +newly-computed hash is compared to the hardcoded value. + +Be aware that any changes you make to the source code included in a production build _after_ the build has been created +will cause the hashes to differ and thus trigger the tamper warning for your users. If there is some intentionally +dynamic aspect to your code, such as personalised/customisable builds, you will need to exclude the relevant files from +the hashing mechanic. + +You can do this by adding the relevant paths to the `tamper_proofing.exclude_files` array in your `config/nativephp.php` +config file. diff --git a/resources/views/docs/1/digging-deeper/updater.md b/resources/views/docs/1/digging-deeper/updater.md new file mode 100644 index 000000000..82be93c86 --- /dev/null +++ b/resources/views/docs/1/digging-deeper/updater.md @@ -0,0 +1,5 @@ +--- +title: Updating Your App +order: 500 +--- +# The Updater diff --git a/resources/views/docs/1/getting-started/introduction.md b/resources/views/docs/1/getting-started/introduction.md new file mode 100644 index 000000000..c1fd604d6 --- /dev/null +++ b/resources/views/docs/1/getting-started/introduction.md @@ -0,0 +1,84 @@ +--- +title: Introduction +order: 001 +--- + +# Hello, NativePHP! + +NativePHP is a new framework for rapidly building rich, native desktop applications using PHP. If you're already a PHP +developer, you'll feel right at home. If you're new to PHP, we think you'll find NativePHP easy to pick up and use. +Whatever your path, we think you're going to be productive quickly. + +NativePHP is taking the world by storm, enabling PHP developers to create true cross-platform, native apps +using the tools and technologies they already know: HTML, CSS, Javascript, and, of course, PHP. + +And they said PHP was dead. + +## What exactly is NativePHP? + +Strictly speaking, NativePHP is a combination of elements: + +1. A collection of easy-to-use classes - abstractions - to enable you to interact with a variety of host operating +system features. +2. A set of tools to enable building and bundling your native application using either the Electron or Tauri browser +environment. +3. A static PHP runtime that allows your app to run on any user's system with zero effort on their part. + +## What NativePHP isn't + +NativePHP is not an especially opinionated way to build native apps. Right now, we only support a Laravel driver, but +we're already working on making it work whatever framework you're using - and even if you're not using a framework at +all. + +NativePHP is not a GUI framework. We don't want to tell you how to build your app. You can choose whatever UI toolset +makes you and your team feel most productive. + +Building a React front-end? No problem. Vue? Sure. Livewire or Inertia? Doesn't matter! Plain old HTML and CSS? +You got it. Tailwind? Bootstrap? Material UI? Whatever you want. + +NativePHP is not some new custom fork of PHP. This is the good old PHP you know and love. + +## What's in the box? + +NativePHP comes with a bunch of useful features out of the box, including: + +- Window management +- Menu management +- File management +- Database support (SQLite) +- Native notifications + +All of this and more is explored in the rest of these docs. + +## What can I build with NativePHP? + +Honestly, anything you want. We believe NativePHP is going to empower thousands of developers to build all kinds of +applications. The only limit is your imagination. + +You could build a menubar app that lets you manage your cron jobs, or a cool new launcher app, or a screen recorder +that puts cowboy hats on every smiley-face emoji it sees. + +(You should totally build that last one.) + +## What's next? + +Go read the docs! We've tried to make them as comprehensive as possible, but if you find something missing, please +feel free to [contribute](https://github.com/nativephp/nativephp.com). + +This site and all the NativePHP are open source and available on [GitHub](https://github.com/nativephp). + +Ready to jump in? [Let's get started](installation). + +## Credits + +NativePHP wouldn't be possible without the following projects and the hard work of all of their wonderful contributors: + +- [PHP](https://php.net) +- [Electron](https://electronjs.org) +- [Tauri](https://tauri.studio) +- [Laravel](https://laravel.com) +- [Symfony](https://symfony.com) +- [Static PHP CLI](https://github.com/crazywhalecc/static-php-cli/) + +NativePHP is a copyright of and maintained by [Marcel Pociot](https://twitter.com/marcelpociot) and +[Simon Hamp](https://twitter.com/simonhamp). diff --git a/resources/views/docs/1/the-basics/clipboard.md b/resources/views/docs/1/the-basics/clipboard.md new file mode 100644 index 000000000..d492cb805 --- /dev/null +++ b/resources/views/docs/1/the-basics/clipboard.md @@ -0,0 +1,48 @@ +--- +title: Clipboard +order: 700 +--- + +## Working with the Clipboard + +NativePHP allows you to easily read from and write to the system clipboard using just PHP, thanks to the `Clipboard` +facade. + +### Reading from the Clipboard + +You can read `text`, `html` or `image` data from the clipboard using the appropriate method: + +```php +use Native\Laravel\Facades\Clipboard; + +Clipboard::text(); +Clipboard::html(); +Clipboard::image(); +``` + +### Writing to the Clipboard + +You can write `text`, `html` or `image` data to the clipboard using the appropriate method: + +```php +use Native\Laravel\Facades\Clipboard; + +Clipboard::text('Some copied text'); +Clipboard::html('
Some copied HTML
'); +Clipboard::image('path/to/image.png'); +``` + +Note that the `image()` method expects a path to an image, not the image data itself. NativePHP will take care of +serializing the image data for you. + +### Clearing the Clipboard + +You may also programmatically clear the clipboard using the `clear()` method. + +```php +use Native\Laravel\Facades\Clipboard; + +Clipboard::clear(); +``` + +This is useful if you need the contents of the clipboard to expire after a certain amount of time. diff --git a/resources/views/docs/1/the-basics/notifications.md b/resources/views/docs/1/the-basics/notifications.md index 39d4e9f59..1c0033802 100644 --- a/resources/views/docs/1/the-basics/notifications.md +++ b/resources/views/docs/1/the-basics/notifications.md @@ -3,6 +3,58 @@ title: Notifications order: 500 --- -## Opening native dialogs +## Native Notifications -NativePHP allows you to open native file dialogs. +NativePHP supports showing native notifications for each platform. When used sparingly, notifications can be a great +way to inform the user about events that are occurring in your application and to bring their attention back to it, +especially if further input from them is required. + +### Showing a Notification + +To show a notification, you can use the `Native\Laravel\Notification` class. The notification will show only when the +`show()` method is called. + +```php +use Native\Laravel\Notification; + +Notification::new() + ->title('Hello, from NativePHP!') + ->show(); +``` + +## Configuring Notifications + +### Notification Title + +You may set the title of the notification using the `title()` method. + +```php +use Native\Laravel\Notification; + +Notification::new() + ->title('NativePHP rocks!') + ->show(); +``` + +### Notification Body + +You may set the body of the notification using the `message()` method. + +```php +use Native\Laravel\Notification; + +Notification::new() + ->title('NativePHP rocks!') + ->message('🔥') + ->show(); +``` + +## Notification Events + +NativePHP provides a simple way to listen for notification events. All events get dispatched as regular Laravel events, +so you may use your `EventServiceProvider` to register listeners. + +### Notification Clicked + +The `Native\Laravel\Events\Notifications\NotificationClicked` event will be dispatched when the user clicks on a +notification shown by your application.