fastqr 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/BUILD.md +482 -0
- data/CHANGELOG.md +51 -0
- data/CMakeLists.txt +126 -0
- data/CONTRIBUTING.md +63 -0
- data/DISTRIBUTION.md +730 -0
- data/INSTALL.md +171 -0
- data/LICENSE +39 -0
- data/PREBUILT.md +171 -0
- data/README.md +312 -0
- data/VERSION +1 -0
- data/bindings/nodejs/binding.gyp +38 -0
- data/bindings/nodejs/fastqr_node.cpp +125 -0
- data/bindings/nodejs/index.d.ts +80 -0
- data/bindings/nodejs/index.js +187 -0
- data/bindings/nodejs/lib/platform.js +72 -0
- data/bindings/nodejs/package.json +45 -0
- data/bindings/nodejs/test/test.js +45 -0
- data/bindings/php/fastqr_php.cpp +85 -0
- data/bindings/php/src/FastQR.php +316 -0
- data/bindings/php/tests/FastQRTest.php +97 -0
- data/bindings/ruby/extconf.rb +29 -0
- data/bindings/ruby/fastqr_ruby.cpp +122 -0
- data/bindings/ruby/lib/fastqr/platform.rb +70 -0
- data/bindings/ruby/lib/fastqr/version.rb +6 -0
- data/bindings/ruby/lib/fastqr.rb +129 -0
- data/bindings/ruby/prebuilt/macos-arm64.tar.gz +1 -0
- data/bindings/ruby/prebuilt/macos-x86_64.tar.gz +1 -0
- data/build.sh +109 -0
- data/cmake/fastqrConfig.cmake.in +6 -0
- data/composer.json +36 -0
- data/docs/CLI_USAGE.md +478 -0
- data/docs/NODEJS_USAGE.md +694 -0
- data/docs/PHP_USAGE.md +815 -0
- data/docs/README.md +191 -0
- data/docs/RUBY_USAGE.md +537 -0
- data/examples/CMakeLists.txt +7 -0
- data/examples/basic.cpp +58 -0
- data/include/fastqr.h +97 -0
- data/include/stb_image.h +7988 -0
- data/include/stb_image_write.h +1724 -0
- data/phpunit.xml +18 -0
- data/prebuilt/README.md +131 -0
- data/scripts/README.md +248 -0
- data/scripts/build-binaries.sh +98 -0
- data/scripts/build-local.sh +18 -0
- data/scripts/install.sh +87 -0
- data/scripts/release.sh +78 -0
- data/scripts/update-version.sh +58 -0
- data/src/cli.cpp +316 -0
- data/src/fastqr.cpp +665 -0
- data/test.sh +86 -0
- metadata +155 -0
data/docs/PHP_USAGE.md
ADDED
|
@@ -0,0 +1,815 @@
|
|
|
1
|
+
# FastQR PHP Usage Guide
|
|
2
|
+
|
|
3
|
+
Complete guide for using FastQR in PHP and Laravel applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
composer require fastqr/fastqr
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Note:** No system dependencies required! Pre-built binaries are included. 🎉
|
|
12
|
+
|
|
13
|
+
**Requirements:**
|
|
14
|
+
- PHP 7.4 or higher
|
|
15
|
+
- FFI extension (usually enabled by default)
|
|
16
|
+
|
|
17
|
+
## Basic Usage
|
|
18
|
+
|
|
19
|
+
```php
|
|
20
|
+
<?php
|
|
21
|
+
require 'vendor/autoload.php';
|
|
22
|
+
|
|
23
|
+
use FastQR\FastQR;
|
|
24
|
+
|
|
25
|
+
// Generate QR code
|
|
26
|
+
FastQR::generate('Hello World', 'qr.png');
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## API Reference
|
|
30
|
+
|
|
31
|
+
### `FastQR::generate($data, $outputPath, $options = [])`
|
|
32
|
+
|
|
33
|
+
Generate a QR code and save to file.
|
|
34
|
+
|
|
35
|
+
**Parameters:**
|
|
36
|
+
- `$data` (string, required) - Data to encode (UTF-8 supported)
|
|
37
|
+
- `$outputPath` (string, required) - Path to save the QR code image
|
|
38
|
+
- `$options` (array, optional) - Generation options
|
|
39
|
+
|
|
40
|
+
**Returns:** `bool` - `true` if successful
|
|
41
|
+
|
|
42
|
+
**Throws:** `RuntimeException` if generation fails
|
|
43
|
+
|
|
44
|
+
### Options
|
|
45
|
+
|
|
46
|
+
| Option | Type | Default | Description |
|
|
47
|
+
|--------|------|---------|-------------|
|
|
48
|
+
| `'size'` | int | `300` | Output size in pixels (QR codes are square) |
|
|
49
|
+
| `'optimizeSize'` | bool | `false` | Auto round-up for best performance |
|
|
50
|
+
| `'width'` | int | - | (Deprecated) Use `'size'` instead |
|
|
51
|
+
| `'height'` | int | - | (Deprecated) Use `'size'` instead |
|
|
52
|
+
| `'foreground'` | array[3] | `[0, 0, 0]` | QR code color (RGB) |
|
|
53
|
+
| `'background'` | array[3] | `[255, 255, 255]` | Background color (RGB) |
|
|
54
|
+
| `'errorLevel'` | string | `'M'` | Error correction: 'L', 'M', 'Q', 'H' |
|
|
55
|
+
| `'logo'` | string | `null` | Path to logo image |
|
|
56
|
+
| `'logoSize'` | int | `20` | Logo size as percentage (1-50) |
|
|
57
|
+
| `'quality'` | int | `95` | Image quality (1-100) |
|
|
58
|
+
| `'format'` | string | `'png'` | Output format: 'png', 'jpg', 'webp' |
|
|
59
|
+
|
|
60
|
+
### `FastQR::generateBatch($dataArray, $outputDir, $options = [])`
|
|
61
|
+
|
|
62
|
+
Generate multiple QR codes at once - **7x faster** than calling `generate` multiple times!
|
|
63
|
+
|
|
64
|
+
**Parameters:**
|
|
65
|
+
- `$dataArray` (array, required) - Array of strings to encode
|
|
66
|
+
- `$outputDir` (string, required) - Directory to save QR codes (created if doesn't exist)
|
|
67
|
+
- `$options` (array, optional) - Same options as `generate`
|
|
68
|
+
|
|
69
|
+
**Returns:** array - `['success' => int, 'failed' => int]`
|
|
70
|
+
|
|
71
|
+
**Example:**
|
|
72
|
+
```php
|
|
73
|
+
$data = ['QR 1', 'QR 2', 'QR 3'];
|
|
74
|
+
$result = FastQR::generateBatch($data, 'output/', ['size' => 500, 'optimizeSize' => true]);
|
|
75
|
+
// Creates: output/1.png, output/2.png, output/3.png
|
|
76
|
+
echo "Generated {$result['success']} QR codes";
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### `FastQR::version()`
|
|
80
|
+
|
|
81
|
+
Get library version.
|
|
82
|
+
|
|
83
|
+
**Returns:** string (e.g., "1.0.0")
|
|
84
|
+
|
|
85
|
+
```php
|
|
86
|
+
echo FastQR::version();
|
|
87
|
+
// => "1.0.0"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Examples
|
|
91
|
+
|
|
92
|
+
### 1. Basic QR Code
|
|
93
|
+
|
|
94
|
+
```php
|
|
95
|
+
<?php
|
|
96
|
+
use FastQR\FastQR;
|
|
97
|
+
|
|
98
|
+
FastQR::generate('https://example.com', 'qr.png');
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 2. Custom Size
|
|
102
|
+
|
|
103
|
+
```php
|
|
104
|
+
FastQR::generate('Large QR', 'large.png', [
|
|
105
|
+
'size' => 1000
|
|
106
|
+
]);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 3. Colored QR Code
|
|
110
|
+
|
|
111
|
+
```php
|
|
112
|
+
// Red QR on yellow background
|
|
113
|
+
FastQR::generate('Colored', 'colored.png', [
|
|
114
|
+
'foreground' => [255, 0, 0], // Red
|
|
115
|
+
'background' => [255, 255, 200] // Light yellow
|
|
116
|
+
]);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 4. QR Code with Logo
|
|
120
|
+
|
|
121
|
+
```php
|
|
122
|
+
FastQR::generate('Company', 'company.png', [
|
|
123
|
+
'size' => 800,
|
|
124
|
+
'logo' => 'logo.png',
|
|
125
|
+
'logoSize' => 25,
|
|
126
|
+
'errorLevel' => 'H' // High error correction for logo
|
|
127
|
+
]);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 5. High Error Correction
|
|
131
|
+
|
|
132
|
+
```php
|
|
133
|
+
FastQR::generate('Important Data', 'qr.png', [
|
|
134
|
+
'errorLevel' => 'H' // ~30% recovery capability
|
|
135
|
+
]);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 6. UTF-8 Support
|
|
139
|
+
|
|
140
|
+
```php
|
|
141
|
+
// Vietnamese
|
|
142
|
+
FastQR::generate('Xin chào Việt Nam! 🇻🇳', 'vietnamese.png');
|
|
143
|
+
|
|
144
|
+
// Japanese
|
|
145
|
+
FastQR::generate('こんにちは日本', 'japanese.png');
|
|
146
|
+
|
|
147
|
+
// Emoji
|
|
148
|
+
FastQR::generate('Hello 👋 World 🌍', 'emoji.png');
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 7. Different Formats
|
|
152
|
+
|
|
153
|
+
```php
|
|
154
|
+
// PNG (default)
|
|
155
|
+
FastQR::generate('Data', 'output.png');
|
|
156
|
+
|
|
157
|
+
// JPEG
|
|
158
|
+
FastQR::generate('Data', 'output.jpg', ['quality' => 90]);
|
|
159
|
+
|
|
160
|
+
// WebP
|
|
161
|
+
FastQR::generate('Data', 'output.webp', ['quality' => 85]);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Laravel Integration
|
|
165
|
+
|
|
166
|
+
### Controller Example
|
|
167
|
+
|
|
168
|
+
```php
|
|
169
|
+
<?php
|
|
170
|
+
|
|
171
|
+
namespace App\Http\Controllers;
|
|
172
|
+
|
|
173
|
+
use FastQR\FastQR;
|
|
174
|
+
use Illuminate\Http\Request;
|
|
175
|
+
use Illuminate\Support\Facades\Storage;
|
|
176
|
+
use Illuminate\Support\Str;
|
|
177
|
+
|
|
178
|
+
class QRCodeController extends Controller
|
|
179
|
+
{
|
|
180
|
+
public function generate(Request $request)
|
|
181
|
+
{
|
|
182
|
+
$validated = $request->validate([
|
|
183
|
+
'data' => 'required|string',
|
|
184
|
+
'size' => 'nullable|integer|min:100|max:5000',
|
|
185
|
+
'height' => 'nullable|integer|min:100|max:5000',
|
|
186
|
+
]);
|
|
187
|
+
|
|
188
|
+
$filename = 'qr_' . Str::random(16) . '.png';
|
|
189
|
+
$path = storage_path('app/public/qrcodes/' . $filename);
|
|
190
|
+
|
|
191
|
+
// Ensure directory exists
|
|
192
|
+
if (!is_dir(dirname($path))) {
|
|
193
|
+
mkdir(dirname($path), 0755, true);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
FastQR::generate($validated['data'], $path, [
|
|
197
|
+
'width' => $validated['width'] ?? 500,
|
|
198
|
+
'height' => $validated['height'] ?? 500,
|
|
199
|
+
'errorLevel' => 'M'
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
return response()->json([
|
|
203
|
+
'success' => true,
|
|
204
|
+
'url' => asset('storage/qrcodes/' . $filename),
|
|
205
|
+
'filename' => $filename
|
|
206
|
+
]);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
public function generateWithLogo(Request $request)
|
|
210
|
+
{
|
|
211
|
+
$validated = $request->validate([
|
|
212
|
+
'data' => 'required|string',
|
|
213
|
+
'logo' => 'required|file|image|max:2048'
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
// Save logo
|
|
217
|
+
$logoPath = $request->file('logo')->store('temp', 'local');
|
|
218
|
+
$fullLogoPath = storage_path('app/' . $logoPath);
|
|
219
|
+
|
|
220
|
+
// Generate QR
|
|
221
|
+
$filename = 'qr_' . time() . '.png';
|
|
222
|
+
$qrPath = storage_path('app/public/qrcodes/' . $filename);
|
|
223
|
+
|
|
224
|
+
FastQR::generate($validated['data'], $qrPath, [
|
|
225
|
+
'size' => 800,
|
|
226
|
+
'height' => 800,
|
|
227
|
+
'logo' => $fullLogoPath,
|
|
228
|
+
'logoSize' => 25,
|
|
229
|
+
'errorLevel' => 'H'
|
|
230
|
+
]);
|
|
231
|
+
|
|
232
|
+
// Clean up logo
|
|
233
|
+
Storage::delete($logoPath);
|
|
234
|
+
|
|
235
|
+
return response()->file($qrPath);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
public function download(Request $request)
|
|
239
|
+
{
|
|
240
|
+
$data = $request->input('data');
|
|
241
|
+
|
|
242
|
+
$filename = 'qr_' . time() . '.png';
|
|
243
|
+
$path = storage_path('app/temp/' . $filename);
|
|
244
|
+
|
|
245
|
+
if (!is_dir(dirname($path))) {
|
|
246
|
+
mkdir(dirname($path), 0755, true);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
FastQR::generate($data, $path, [
|
|
250
|
+
'size' => 600,
|
|
251
|
+
'height' => 600
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
return response()->download($path)->deleteFileAfterSend(true);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Routes
|
|
260
|
+
|
|
261
|
+
```php
|
|
262
|
+
<?php
|
|
263
|
+
|
|
264
|
+
use App\Http\Controllers\QRCodeController;
|
|
265
|
+
|
|
266
|
+
Route::prefix('api/qr')->group(function () {
|
|
267
|
+
Route::post('/generate', [QRCodeController::class, 'generate']);
|
|
268
|
+
Route::post('/with-logo', [QRCodeController::class, 'generateWithLogo']);
|
|
269
|
+
Route::post('/download', [QRCodeController::class, 'download']);
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Service Class
|
|
274
|
+
|
|
275
|
+
```php
|
|
276
|
+
<?php
|
|
277
|
+
|
|
278
|
+
namespace App\Services;
|
|
279
|
+
|
|
280
|
+
use FastQR\FastQR;
|
|
281
|
+
use Illuminate\Support\Str;
|
|
282
|
+
|
|
283
|
+
class QRCodeService
|
|
284
|
+
{
|
|
285
|
+
protected $outputPath;
|
|
286
|
+
|
|
287
|
+
public function __construct()
|
|
288
|
+
{
|
|
289
|
+
$this->outputPath = storage_path('app/public/qrcodes');
|
|
290
|
+
|
|
291
|
+
if (!is_dir($this->outputPath)) {
|
|
292
|
+
mkdir($this->outputPath, 0755, true);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
public function generate(string $data, array $options = []): string
|
|
297
|
+
{
|
|
298
|
+
$filename = 'qr_' . Str::random(16) . '.png';
|
|
299
|
+
$filepath = $this->outputPath . '/' . $filename;
|
|
300
|
+
|
|
301
|
+
$defaultOptions = [
|
|
302
|
+
'size' => 500,
|
|
303
|
+
'height' => 500,
|
|
304
|
+
'errorLevel' => 'M'
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
FastQR::generate($data, $filepath, array_merge($defaultOptions, $options));
|
|
308
|
+
|
|
309
|
+
return $filename;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
public function generateWithLogo(string $data, string $logoPath, array $options = []): string
|
|
313
|
+
{
|
|
314
|
+
$options['logo'] = $logoPath;
|
|
315
|
+
$options['logoSize'] = $options['logoSize'] ?? 25;
|
|
316
|
+
$options['errorLevel'] = 'H';
|
|
317
|
+
|
|
318
|
+
return $this->generate($data, $options);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
public function getPath(string $filename): string
|
|
322
|
+
{
|
|
323
|
+
return $this->outputPath . '/' . $filename;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
public function getUrl(string $filename): string
|
|
327
|
+
{
|
|
328
|
+
return asset('storage/qrcodes/' . $filename);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
public function delete(string $filename): bool
|
|
332
|
+
{
|
|
333
|
+
$filepath = $this->getPath($filename);
|
|
334
|
+
|
|
335
|
+
if (file_exists($filepath)) {
|
|
336
|
+
return unlink($filepath);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
public function cleanup(int $olderThanDays = 7): int
|
|
343
|
+
{
|
|
344
|
+
$deleted = 0;
|
|
345
|
+
$threshold = time() - ($olderThanDays * 24 * 60 * 60);
|
|
346
|
+
|
|
347
|
+
$files = glob($this->outputPath . '/qr_*.png');
|
|
348
|
+
|
|
349
|
+
foreach ($files as $file) {
|
|
350
|
+
if (filemtime($file) < $threshold) {
|
|
351
|
+
if (unlink($file)) {
|
|
352
|
+
$deleted++;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return $deleted;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Model Integration
|
|
363
|
+
|
|
364
|
+
```php
|
|
365
|
+
<?php
|
|
366
|
+
|
|
367
|
+
namespace App\Models;
|
|
368
|
+
|
|
369
|
+
use Illuminate\Database\Eloquent\Model;
|
|
370
|
+
use FastQR\FastQR;
|
|
371
|
+
|
|
372
|
+
class Event extends Model
|
|
373
|
+
{
|
|
374
|
+
protected $fillable = ['name', 'date', 'location', 'url', 'qr_code'];
|
|
375
|
+
|
|
376
|
+
protected static function boot()
|
|
377
|
+
{
|
|
378
|
+
parent::boot();
|
|
379
|
+
|
|
380
|
+
static::created(function ($event) {
|
|
381
|
+
$event->generateQRCode();
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
public function generateQRCode()
|
|
386
|
+
{
|
|
387
|
+
$filename = 'event_' . $this->id . '.png';
|
|
388
|
+
$path = storage_path('app/public/qrcodes/' . $filename);
|
|
389
|
+
|
|
390
|
+
if (!is_dir(dirname($path))) {
|
|
391
|
+
mkdir(dirname($path), 0755, true);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
$eventInfo = "Event: {$this->name}\n";
|
|
395
|
+
$eventInfo .= "Date: {$this->date->format('d/m/Y')}\n";
|
|
396
|
+
$eventInfo .= "Location: {$this->location}\n";
|
|
397
|
+
$eventInfo .= "URL: {$this->url}";
|
|
398
|
+
|
|
399
|
+
FastQR::generate($eventInfo, $path, [
|
|
400
|
+
'size' => 600,
|
|
401
|
+
'height' => 600,
|
|
402
|
+
'errorLevel' => 'H'
|
|
403
|
+
]);
|
|
404
|
+
|
|
405
|
+
$this->update(['qr_code' => $filename]);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public function getQRCodeUrlAttribute()
|
|
409
|
+
{
|
|
410
|
+
return $this->qr_code ? asset('storage/qrcodes/' . $this->qr_code) : null;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Job Example
|
|
416
|
+
|
|
417
|
+
```php
|
|
418
|
+
<?php
|
|
419
|
+
|
|
420
|
+
namespace App\Jobs;
|
|
421
|
+
|
|
422
|
+
use Illuminate\Bus\Queueable;
|
|
423
|
+
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
424
|
+
use Illuminate\Foundation\Bus\Dispatchable;
|
|
425
|
+
use Illuminate\Queue\InteractsWithQueue;
|
|
426
|
+
use Illuminate\Queue\SerializesModels;
|
|
427
|
+
use App\Models\User;
|
|
428
|
+
use FastQR\FastQR;
|
|
429
|
+
|
|
430
|
+
class GenerateUserQRCode implements ShouldQueue
|
|
431
|
+
{
|
|
432
|
+
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
433
|
+
|
|
434
|
+
protected $userId;
|
|
435
|
+
|
|
436
|
+
public function __construct(int $userId)
|
|
437
|
+
{
|
|
438
|
+
$this->userId = $userId;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
public function handle()
|
|
442
|
+
{
|
|
443
|
+
$user = User::findOrFail($this->userId);
|
|
444
|
+
|
|
445
|
+
$vcard = "BEGIN:VCARD\n";
|
|
446
|
+
$vcard .= "VERSION:3.0\n";
|
|
447
|
+
$vcard .= "FN:{$user->name}\n";
|
|
448
|
+
$vcard .= "EMAIL:{$user->email}\n";
|
|
449
|
+
$vcard .= "TEL:{$user->phone}\n";
|
|
450
|
+
$vcard .= "END:VCARD";
|
|
451
|
+
|
|
452
|
+
$filename = 'user_' . $user->id . '.png';
|
|
453
|
+
$path = storage_path('app/public/qrcodes/' . $filename);
|
|
454
|
+
|
|
455
|
+
FastQR::generate($vcard, $path, [
|
|
456
|
+
'size' => 600,
|
|
457
|
+
'height' => 600,
|
|
458
|
+
'errorLevel' => 'H'
|
|
459
|
+
]);
|
|
460
|
+
|
|
461
|
+
$user->update(['qr_code' => $filename]);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Command Example
|
|
467
|
+
|
|
468
|
+
```php
|
|
469
|
+
<?php
|
|
470
|
+
|
|
471
|
+
namespace App\Console\Commands;
|
|
472
|
+
|
|
473
|
+
use Illuminate\Console\Command;
|
|
474
|
+
use FastQR\FastQR;
|
|
475
|
+
|
|
476
|
+
class GenerateQRCode extends Command
|
|
477
|
+
{
|
|
478
|
+
protected $signature = 'qr:generate {data} {filename?}';
|
|
479
|
+
protected $description = 'Generate a QR code';
|
|
480
|
+
|
|
481
|
+
public function handle()
|
|
482
|
+
{
|
|
483
|
+
$data = $this->argument('data');
|
|
484
|
+
$filename = $this->argument('filename') ?? 'qr_' . time() . '.png';
|
|
485
|
+
$path = storage_path('app/public/qrcodes/' . $filename);
|
|
486
|
+
|
|
487
|
+
if (!is_dir(dirname($path))) {
|
|
488
|
+
mkdir(dirname($path), 0755, true);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
FastQR::generate($data, $path, [
|
|
493
|
+
'size' => 600,
|
|
494
|
+
'height' => 600
|
|
495
|
+
]);
|
|
496
|
+
|
|
497
|
+
$this->info("✓ QR code generated: {$filename}");
|
|
498
|
+
$this->info(" Path: {$path}");
|
|
499
|
+
} catch (\Exception $e) {
|
|
500
|
+
$this->error("✗ Failed: " . $e->getMessage());
|
|
501
|
+
return 1;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return 0;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## WordPress Integration
|
|
510
|
+
|
|
511
|
+
### Plugin Example
|
|
512
|
+
|
|
513
|
+
```php
|
|
514
|
+
<?php
|
|
515
|
+
/**
|
|
516
|
+
* Plugin Name: FastQR Generator
|
|
517
|
+
* Description: Generate QR codes using FastQR
|
|
518
|
+
* Version: 1.0.0
|
|
519
|
+
*/
|
|
520
|
+
|
|
521
|
+
require_once plugin_dir_path(__FILE__) . 'vendor/autoload.php';
|
|
522
|
+
|
|
523
|
+
use FastQR\FastQR;
|
|
524
|
+
|
|
525
|
+
// Add admin menu
|
|
526
|
+
add_action('admin_menu', 'fastqr_add_admin_menu');
|
|
527
|
+
|
|
528
|
+
function fastqr_add_admin_menu() {
|
|
529
|
+
add_menu_page(
|
|
530
|
+
'FastQR Generator',
|
|
531
|
+
'QR Codes',
|
|
532
|
+
'manage_options',
|
|
533
|
+
'fastqr-generator',
|
|
534
|
+
'fastqr_admin_page',
|
|
535
|
+
'dashicons-grid-view'
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Admin page
|
|
540
|
+
function fastqr_admin_page() {
|
|
541
|
+
if (isset($_POST['generate_qr'])) {
|
|
542
|
+
$data = sanitize_text_field($_POST['qr_data']);
|
|
543
|
+
$filename = 'qr_' . time() . '.png';
|
|
544
|
+
$upload_dir = wp_upload_dir();
|
|
545
|
+
$qr_path = $upload_dir['path'] . '/' . $filename;
|
|
546
|
+
|
|
547
|
+
FastQR::generate($data, $qr_path, [
|
|
548
|
+
'size' => 500,
|
|
549
|
+
'height' => 500
|
|
550
|
+
]);
|
|
551
|
+
|
|
552
|
+
$qr_url = $upload_dir['url'] . '/' . $filename;
|
|
553
|
+
|
|
554
|
+
echo '<div class="notice notice-success"><p>QR Code generated!</p></div>';
|
|
555
|
+
echo '<img src="' . esc_url($qr_url) . '" alt="QR Code" />';
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
?>
|
|
559
|
+
<div class="wrap">
|
|
560
|
+
<h1>Generate QR Code</h1>
|
|
561
|
+
<form method="post">
|
|
562
|
+
<table class="form-table">
|
|
563
|
+
<tr>
|
|
564
|
+
<th><label for="qr_data">Data</label></th>
|
|
565
|
+
<td>
|
|
566
|
+
<textarea id="qr_data" name="qr_data" rows="5" cols="50" required></textarea>
|
|
567
|
+
</td>
|
|
568
|
+
</tr>
|
|
569
|
+
</table>
|
|
570
|
+
<?php submit_button('Generate QR Code', 'primary', 'generate_qr'); ?>
|
|
571
|
+
</form>
|
|
572
|
+
</div>
|
|
573
|
+
<?php
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Shortcode
|
|
577
|
+
add_shortcode('fastqr', 'fastqr_shortcode');
|
|
578
|
+
|
|
579
|
+
function fastqr_shortcode($atts) {
|
|
580
|
+
$atts = shortcode_atts([
|
|
581
|
+
'data' => '',
|
|
582
|
+
'size' => 300,
|
|
583
|
+
'height' => 300
|
|
584
|
+
], $atts);
|
|
585
|
+
|
|
586
|
+
if (empty($atts['data'])) {
|
|
587
|
+
return '';
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
$filename = 'qr_' . md5($atts['data']) . '.png';
|
|
591
|
+
$upload_dir = wp_upload_dir();
|
|
592
|
+
$qr_path = $upload_dir['path'] . '/' . $filename;
|
|
593
|
+
$qr_url = $upload_dir['url'] . '/' . $filename;
|
|
594
|
+
|
|
595
|
+
if (!file_exists($qr_path)) {
|
|
596
|
+
FastQR::generate($atts['data'], $qr_path, [
|
|
597
|
+
'width' => intval($atts['width']),
|
|
598
|
+
'height' => intval($atts['height'])
|
|
599
|
+
]);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return '<img src="' . esc_url($qr_url) . '" alt="QR Code" class="fastqr-image" />';
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
Usage in WordPress:
|
|
607
|
+
```
|
|
608
|
+
[fastqr data="https://example.com" width="400" height="400"]
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
## Plain PHP Examples
|
|
612
|
+
|
|
613
|
+
### Simple Script
|
|
614
|
+
|
|
615
|
+
```php
|
|
616
|
+
<?php
|
|
617
|
+
require 'vendor/autoload.php';
|
|
618
|
+
|
|
619
|
+
use FastQR\FastQR;
|
|
620
|
+
|
|
621
|
+
// Get data from form
|
|
622
|
+
$data = $_POST['data'] ?? 'Default Data';
|
|
623
|
+
|
|
624
|
+
// Generate QR code
|
|
625
|
+
$filename = 'qr_' . time() . '.png';
|
|
626
|
+
$path = __DIR__ . '/qrcodes/' . $filename;
|
|
627
|
+
|
|
628
|
+
// Ensure directory exists
|
|
629
|
+
if (!is_dir(__DIR__ . '/qrcodes')) {
|
|
630
|
+
mkdir(__DIR__ . '/qrcodes', 0755, true);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
FastQR::generate($data, $path, [
|
|
635
|
+
'width' => 500,
|
|
636
|
+
'height' => 500
|
|
637
|
+
]);
|
|
638
|
+
|
|
639
|
+
echo "QR code generated: <a href='qrcodes/{$filename}'>Download</a><br>";
|
|
640
|
+
echo "<img src='qrcodes/{$filename}' alt='QR Code' />";
|
|
641
|
+
} catch (Exception $e) {
|
|
642
|
+
echo "Error: " . $e->getMessage();
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
### API Endpoint
|
|
647
|
+
|
|
648
|
+
```php
|
|
649
|
+
<?php
|
|
650
|
+
require 'vendor/autoload.php';
|
|
651
|
+
|
|
652
|
+
use FastQR\FastQR;
|
|
653
|
+
|
|
654
|
+
header('Content-Type: application/json');
|
|
655
|
+
|
|
656
|
+
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
657
|
+
http_response_code(405);
|
|
658
|
+
echo json_encode(['error' => 'Method not allowed']);
|
|
659
|
+
exit;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
$input = json_decode(file_get_contents('php://input'), true);
|
|
663
|
+
$data = $input['data'] ?? null;
|
|
664
|
+
|
|
665
|
+
if (empty($data)) {
|
|
666
|
+
http_response_code(400);
|
|
667
|
+
echo json_encode(['error' => 'Data is required']);
|
|
668
|
+
exit;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
try {
|
|
672
|
+
$filename = 'qr_' . uniqid() . '.png';
|
|
673
|
+
$path = __DIR__ . '/qrcodes/' . $filename;
|
|
674
|
+
|
|
675
|
+
if (!is_dir(dirname($path))) {
|
|
676
|
+
mkdir(dirname($path), 0755, true);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
$options = [
|
|
680
|
+
'width' => $input['width'] ?? 500,
|
|
681
|
+
'height' => $input['height'] ?? 500,
|
|
682
|
+
'errorLevel' => $input['errorLevel'] ?? 'M'
|
|
683
|
+
];
|
|
684
|
+
|
|
685
|
+
FastQR::generate($data, $path, $options);
|
|
686
|
+
|
|
687
|
+
echo json_encode([
|
|
688
|
+
'success' => true,
|
|
689
|
+
'url' => '/qrcodes/' . $filename,
|
|
690
|
+
'filename' => $filename
|
|
691
|
+
]);
|
|
692
|
+
} catch (Exception $e) {
|
|
693
|
+
http_response_code(500);
|
|
694
|
+
echo json_encode(['error' => $e->getMessage()]);
|
|
695
|
+
}
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
## Error Handling
|
|
699
|
+
|
|
700
|
+
```php
|
|
701
|
+
<?php
|
|
702
|
+
use FastQR\FastQR;
|
|
703
|
+
|
|
704
|
+
try {
|
|
705
|
+
FastQR::generate('Data', 'output.png', [
|
|
706
|
+
'width' => 1000,
|
|
707
|
+
'height' => 1000
|
|
708
|
+
]);
|
|
709
|
+
echo "✓ QR code generated successfully\n";
|
|
710
|
+
} catch (RuntimeException $e) {
|
|
711
|
+
echo "✗ Failed to generate QR code: " . $e->getMessage() . "\n";
|
|
712
|
+
// Log error
|
|
713
|
+
error_log("QR generation failed: " . $e->getMessage());
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
## Testing
|
|
718
|
+
|
|
719
|
+
### PHPUnit Example
|
|
720
|
+
|
|
721
|
+
```php
|
|
722
|
+
<?php
|
|
723
|
+
use PHPUnit\Framework\TestCase;
|
|
724
|
+
use FastQR\FastQR;
|
|
725
|
+
|
|
726
|
+
class FastQRTest extends TestCase
|
|
727
|
+
{
|
|
728
|
+
private $outputPath;
|
|
729
|
+
|
|
730
|
+
protected function setUp(): void
|
|
731
|
+
{
|
|
732
|
+
$this->outputPath = __DIR__ . '/test_qr.png';
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
protected function tearDown(): void
|
|
736
|
+
{
|
|
737
|
+
if (file_exists($this->outputPath)) {
|
|
738
|
+
unlink($this->outputPath);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
public function testGenerateBasicQR()
|
|
743
|
+
{
|
|
744
|
+
$result = FastQR::generate('Test Data', $this->outputPath);
|
|
745
|
+
|
|
746
|
+
$this->assertTrue($result);
|
|
747
|
+
$this->assertFileExists($this->outputPath);
|
|
748
|
+
$this->assertGreaterThan(0, filesize($this->outputPath));
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
public function testGenerateWithOptions()
|
|
752
|
+
{
|
|
753
|
+
$result = FastQR::generate('Test', $this->outputPath, [
|
|
754
|
+
'size' => 500,
|
|
755
|
+
'height' => 500,
|
|
756
|
+
'errorLevel' => 'H'
|
|
757
|
+
]);
|
|
758
|
+
|
|
759
|
+
$this->assertTrue($result);
|
|
760
|
+
$this->assertFileExists($this->outputPath);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
public function testEmptyDataThrowsException()
|
|
764
|
+
{
|
|
765
|
+
$this->expectException(RuntimeException::class);
|
|
766
|
+
FastQR::generate('', $this->outputPath);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
public function testVersion()
|
|
770
|
+
{
|
|
771
|
+
$version = FastQR::version();
|
|
772
|
+
$this->assertMatchesRegularExpression('/^\d+\.\d+\.\d+$/', $version);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
## Performance Tips
|
|
778
|
+
|
|
779
|
+
- Cache generated QR codes using data hash as filename
|
|
780
|
+
- Use lower quality for web display (`quality` => 75)
|
|
781
|
+
- Use PNG for best quality, JPG for smaller files
|
|
782
|
+
- Clean up old temporary files regularly
|
|
783
|
+
|
|
784
|
+
## Troubleshooting
|
|
785
|
+
|
|
786
|
+
### "Library not found" Error
|
|
787
|
+
|
|
788
|
+
The package includes pre-built binaries. If you see this error, check PHP FFI:
|
|
789
|
+
|
|
790
|
+
```php
|
|
791
|
+
<?php
|
|
792
|
+
// Check if FFI is enabled
|
|
793
|
+
if (!extension_loaded('ffi')) {
|
|
794
|
+
echo "FFI extension is not enabled\n";
|
|
795
|
+
} else {
|
|
796
|
+
echo "FFI extension is enabled\n";
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
### File Permission Errors
|
|
801
|
+
|
|
802
|
+
Ensure output directory has write permissions:
|
|
803
|
+
|
|
804
|
+
```bash
|
|
805
|
+
chmod 755 storage/app/public/qrcodes
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
## See Also
|
|
809
|
+
|
|
810
|
+
- [CLI Usage](CLI_USAGE.md) - Command-line usage
|
|
811
|
+
- [Ruby Usage](RUBY_USAGE.md) - Ruby/Rails guide
|
|
812
|
+
- [Node.js Usage](NODEJS_USAGE.md) - Node.js guide
|
|
813
|
+
- [GitHub Repository](https://github.com/tranhuucanh/fastqr)
|
|
814
|
+
- [Packagist Package](https://packagist.org/packages/fastqr/fastqr)
|
|
815
|
+
|