Skip to content

Commit 1ccfba9

Browse files
init
0 parents  commit 1ccfba9

418 files changed

Lines changed: 25699 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Docs/.github/workflows/deploy.yml
2+
3+
name: Deploy Hugo site to Pages
4+
5+
on:
6+
push:
7+
branches:
8+
- main
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
with:
17+
submodules: true
18+
19+
- name: Setup Hugo
20+
uses: peaceiris/actions-hugo@v3
21+
with:
22+
hugo-version: 'latest'
23+
24+
- name: Build
25+
# ✅ This is the key change!
26+
working-directory: ./Docs
27+
run: hugo --minify
28+
29+
# ... rest of the file remains the same ...
30+
- name: Setup Pages
31+
uses: actions/configure-pages@v5
32+
33+
- name: Upload artifact
34+
uses: actions/upload-pages-artifact@v3
35+
with:
36+
# This path is relative to the repository root
37+
path: ./Docs/public

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc
9+
build/
10+
PHP.xcframework/
11+
Docs/.build

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "Docs/themes/hugo-book"]
2+
path = Docs/themes/hugo-book
3+
url = https://github.com/alex-shpak/hugo-book

.swift-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6.2.0

.vscode/settings.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"files.associations": {
3+
"tsrm.h": "c",
4+
"zend_sort.h": "c",
5+
"zend_api.h": "c",
6+
"zend_exceptions.h": "c",
7+
"typeinfo": "c"
8+
}
9+
}

Docs/.hugo_build.lock

Whitespace-only changes.

Docs/archetypes/default.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
+++
2+
date = '{{ .Date }}'
3+
draft = true
4+
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
5+
+++

Docs/content/_index.md

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
+++
2+
draft = false
3+
title = 'Hello Swift'
4+
+++
5+
6+
SwiftPHP is an effort to make PHP extensions in a safer language than C. Every PHP minor release contains memory leaks and buffer overflow which is easy to create in C, but harder to do the same in Swift. Swift also has great concurrency constructs, allowing you to access more compute in your native extensions.
7+
8+
I have chosen to make the API close to the C API for the first release so anyone coming from the C API or wants to use the C API for documentation can. It also makes migrating existing C extensions easier.
9+
10+
Why not "X-language"? Well, PHP's C-API is mostly C-Macros which is not possible to replicate in any other language other than C++.
11+
12+
- **Zig** - I have created a prototype to replicate many of the C-Macros however its too agressive in its C-Interop that its cause so much work and custom PHP C Core patches than its worth. Zig is also volitile in its API
13+
- **Rust** - Someone already did this work in Rust https://github.com/davidcole1340/ext-php-rs
14+
15+
## Supported PHP Versions
16+
17+
- PHP 8.2 (Unsupported)
18+
- PHP 8.3 (Unsupported)
19+
- PHP 8.4 Thread-safe (ZTS)
20+
- PHP 8.5 (Pending Testing)
21+
22+
## Supported Operating Systems
23+
24+
- MacOS ARM64
25+
- Windows 11 x64 and ARM64
26+
- Windows 11 ARM64
27+
- Linux x64
28+
- Linux ARM64
29+
30+
## Hello World Skeleton
31+
32+
```bash
33+
# Compile extension
34+
swift build -v --product SwiftPHPExtension
35+
36+
# Run PHP with custom extension
37+
php -dextension=.build/arm64-apple-macosx/debug/libSwiftPHPExtension.dylib \
38+
-r 'var_dump(confirm_myext_compiled(), myext_hello());'
39+
40+
# Output:
41+
# string(68) "Congratulations! You have successfully compiled the Swift extension."
42+
# string(0) ""
43+
```
44+
45+
### PHP function:
46+
```php
47+
function myext_hello(?string $str): string
48+
{
49+
if ($str === null) {
50+
return "";
51+
}
52+
53+
return "Hello " . $str;
54+
}
55+
```
56+
57+
### Native PHP Extension version in Swift
58+
```swift
59+
import PHPCore
60+
import Foundation
61+
62+
// PHP function argument register for type checking
63+
@MainActor
64+
public let arginfo_myext_hello: [zend_internal_arg_info] =
65+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(
66+
name: "myext_hello",
67+
return_reference: false,
68+
required_num_args: 0, // All parameters are optional
69+
type: UInt32(IS_STRING),
70+
allow_null: false
71+
)
72+
+ [ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(
73+
pass_by_ref: false,
74+
name: "str",
75+
type_hint: UInt32(IS_STRING),
76+
allow_null: true,
77+
default_value: "\"\"")]
78+
79+
// Your Swift function to register
80+
@_cdecl("zif_myext_hello")
81+
public func zif_myext_hello(
82+
execute_data: UnsafeMutablePointer<zend_execute_data>?,
83+
return_value: UnsafeMutablePointer<zval>?) {
84+
// Ensure return value is initialized (redundent but needed)
85+
guard let return_value: UnsafeMutablePointer<zval> = return_value else {
86+
return
87+
}
88+
89+
// Safely do parameter capture
90+
var var_str: UnsafeMutablePointer<CChar>? = nil
91+
var var_len: Int = 0
92+
do {
93+
// Start parameter parsing
94+
guard var state: ParseState = ZEND_PARSE_PARAMETERS_START(
95+
min: 0, max: 1, execute_data: execute_data
96+
) else {
97+
return
98+
}
99+
100+
// Any parameter parsed after this is optional
101+
Z_PARAM_OPTIONAL(state: &state)
102+
103+
// If this was not optional Z_PARAM_STRING
104+
// would be the correct call instead.
105+
try Z_PARAM_STRING_OR_NULL(
106+
state: &state, dest: &var_str, destLen: &var_len
107+
)
108+
109+
try ZEND_PARSE_PARAMETERS_END(state: state)
110+
} catch {
111+
return
112+
}
113+
114+
let swiftString: String
115+
if let cString = var_str {
116+
// A string (even an empty one) was passed, so we use it.
117+
swiftString = String(cString: cString)
118+
} else {
119+
// A `null` was passed or the argument was omitted. Return an empty string
120+
RETURN_STR(ZSTR_EMPTY_ALLOC(), return_value)
121+
return
122+
}
123+
124+
// Format Swift String
125+
let message: String = "Hello \(swiftString)"
126+
127+
// Convert back to PHP String
128+
let retval: UnsafeMutablePointer<zend_string>? = message.withCString {
129+
return zend_string_init(messagePtr, message.utf8.count, false)
130+
}
131+
132+
// Return the PHP String
133+
if let resultString: UnsafeMutablePointer<zend_string> = retval {
134+
RETURN_STR(resultString, return_value)
135+
}
136+
}
137+
138+
// Global pointers to hold data that will persist
139+
@MainActor var myext_functions_ptr: UnsafeMutablePointer<zend_function_entry>? = nil
140+
@MainActor var myext_ini_entries_ptr: UnsafeMutablePointer<zend_ini_entry>? = nil
141+
@MainActor var myext_deps_ptr: UnsafeMutablePointer<zend_module_dep>? = nil
142+
@MainActor var myextModule_ptr: UnsafeMutablePointer<zend_module_entry>? = nil
143+
@MainActor var myext_globals_id: ts_rsrc_id = 0
144+
145+
struct myextGlobals {
146+
var someGlobalVariable: Int = 0
147+
}
148+
149+
@_cdecl("zm_startup_myext")
150+
func zm_startup_myext(type: Int32, module_number: Int32) -> Int32 {
151+
return Int32(SUCCESS.rawValue)
152+
}
153+
154+
@_cdecl("zm_shutdown_myext")
155+
func zm_shutdown_myext(type: Int32, module_number: Int32) -> Int32 {
156+
return Int32(SUCCESS.rawValue)
157+
}
158+
159+
@_cdecl("zm_activate_myext")
160+
func zm_activate_myext(type: Int32, module_number: Int32) -> Int32 {
161+
return Int32(SUCCESS.rawValue)
162+
}
163+
164+
@_cdecl("zm_deactivate_myext")
165+
func zm_deactivate_myext(type: Int32, module_number: Int32) -> Int32 {
166+
return Int32(SUCCESS.rawValue)
167+
}
168+
169+
@_cdecl("zm_info_myext")
170+
func zm_info_myext(zend_module: UnsafeMutableRawPointer?) {
171+
print("Myext Module Version: 2.0.0")
172+
}
173+
174+
@_cdecl("zm_globals_ctor_myext")
175+
func zm_globals_ctor_myext(pointer: UnsafeMutableRawPointer?) {
176+
let globals = pointer!.bindMemory(to: myextGlobals.self, capacity: 1)
177+
globals.pointee.someGlobalVariable = 42
178+
}
179+
180+
@_cdecl("zm_globals_dtor_myext")
181+
func zm_globals_dtor_myext(pointer: UnsafeMutableRawPointer?) {
182+
// Optional cleanup code for globals
183+
}
184+
185+
186+
@_cdecl("get_module")
187+
@MainActor
188+
func get_module() -> UnsafeMutablePointer<zend_module_entry> {
189+
// Allocate memory for myext_functions
190+
var builder = FunctionListBuilder()
191+
192+
// Add myext_hello
193+
builder.add(
194+
name: "myext_hello",
195+
handler: zif_myext_hello,
196+
arg_info: arginfo_myext_hello
197+
)
198+
199+
// Convert to `UnsafeMutablePointer<zend_function_entry>`
200+
myext_functions_ptr = builder.build()
201+
202+
// Create PHP Build String
203+
let version = strdup("1.0.0")
204+
let module_name = strdup("myext")
205+
var buildIdString = "API\(PHP_API_VERSION)"
206+
if ZTS != 0 {
207+
buildIdString += ",TS" // Thread Safe
208+
} else {
209+
buildIdString += ",NTS" // Non-Thread Safe
210+
}
211+
if PHP_DEBUG != 0 {
212+
buildIdString += ",debug"
213+
}
214+
let build_id = strdup(buildIdString)
215+
216+
// Setup Custom INI settings
217+
myext_ini_entries_ptr = UnsafeMutablePointer<zend_ini_entry>.allocate(capacity: 1)
218+
myext_ini_entries_ptr?.initialize(to: zend_ini_entry())
219+
220+
// Dependancies
221+
myext_deps_ptr = UnsafeMutablePointer<zend_module_dep>.allocate(capacity: 1)
222+
myext_deps_ptr?.initialize(to: zend_module_dep())
223+
224+
myextModule_ptr = create_module_entry(
225+
module_name,
226+
version,
227+
myext_functions_ptr,
228+
zm_startup_myext,
229+
zm_shutdown_myext,
230+
zm_activate_myext,
231+
zm_deactivate_myext,
232+
zm_info_myext,
233+
MemoryLayout<myextGlobals>.size,
234+
&myext_globals_id,
235+
zm_globals_ctor_myext,
236+
zm_globals_dtor_myext,
237+
build_id
238+
)
239+
240+
return myextModule_ptr!
241+
}
242+
243+
244+
245+
```

Docs/content/guides/_index.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: "Guides"
3+
---
4+
5+
This is the main landing page for the guides section.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
+++
2+
draft = true
3+
title = 'Installtion Guide'
4+
+++
5+
6+
# Installation Guide
7+
8+
As of writing, this is designed for PHP 8.4 support.
9+
10+
## Installing on macOS
11+
12+
macOS is straight forward but requires brew and xcode installed.
13+
14+
```bash
15+
xcode-select --install
16+
17+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
18+
19+
# Build PHP.xcframework to compile extensions against
20+
./Scripts/build_xcframework.sh
21+
22+
# Build PHP CLI to test your extension with
23+
./Scripts/build_macos.sh
24+
```
25+
26+
## Installation on Windows 11 x64/ARM64
27+
28+
As of this time, the current Windows SDK is broken and you need to follow the guide to install Swift on Windows successfully https://forums.swift.org/t/an-unofficial-guide-to-building-the-swift-toolchain-on-windows-x64-and-arm64/81751

0 commit comments

Comments
 (0)