在我告诉您更多有关如何创建使用PHP的WebSocket服务器以提供在线Web应用程序和本地热线打印机之间的通信访问的信息之前。
捐赠者:
GitHub赞助商:
上一个故事...
你好朋克!我是自由泳者,许多关于 如何从在线Web应用到本地打印机“ Thermal”?。通常在POS应用中使用的热打印机,听起来像Whaaaaat ....(只是开玩笑,什么都没有!)
对不起...自恋(在我的脑海中),但是的,(健康顺便说一句)。所以, 我的收据是什么?
收据(要求)
在很久以前的AM PHP开发人员中,然后我与JavaScript(并爱上了它)有 Backstreet 关系(尝试使其像黑客一样),现在我是Playgrammer。 /p>
框架
他们两个都像文档一样。
当很好时,很好。当不好的时候,总比没有好。 - 迪克·布兰登
依赖项
- React useWebSocket Hook Websocket客户与他的妈妈交谈
- 客户可以写作作业并与他的妈妈分享的ESCPOS-PHP空白纸
- Dexie.js索引端客户端以存储打印机设置(可选)
设备
- 您的计算机 /笔记本电脑(不!)< / li>
- Abiaoqian与ESCPOS-PHP Package1111111111
其他要求
- 咖啡 - 必需!
- 雪茄 - 必需!
- 您非常讨厌的音乐! - 必需!
打开门
Tok Tok Tok ...请打开门。打开不寻常的文本编辑器和开放终端窗口,然后创建您的全新React应用程序! (仅供参考:我不会告诉您如何创建React全新React应用程序,只需阅读他们的文档即可!)
创建全新的React应用程序后,然后使用Composer安装react-use-websocket
(这是错误的方法!)。
这只是我在项目中使用的(f)示例,所以不要以不同的方式询问。刚刚以您的想象力写。
创建打印机设置存储
// Filename: src/db/index.js
import Dexie from 'dexie';
export const db = new Dexie('YOUR-STORAGE-NAME');
db.version(1).stores({
printer_settings: '++id, app_name, printer_name, interface, font_type, ch_line_1, ch_line_2, ch_line_3, cf_line_1, cf_line_2, cf_line_3, cl_operator, cl_time, cl_trx_number, cl_costumer_name, cl_discount, cl_total_discount, cl_total, cl_tax, cl_member, cl_paid, cl_return, cl_loan, cl_saving, cl_tempo, line_feed_each_in_items, column_width, more_new_line, pull_cash_drawer, template'
});
创建打印机设置页面
创建打印机设置页面以允许用户设置自己的语言以及收据的外观。
打印机设置页面包含类似Dexie存储定义的字段。
// Filename: src/constants/index.js
// NOTE:
// - cl = Custom Language
// - ch = Custom Header
// - cf = Custom Footer
export const DEFAULT_PRINTER_SETTING = {
app_name: 'Your Application Name',
printer_name: '/dev/usb/lp0',
interface: 'linux-usb',
font_type: 'C',
ch_line_1: 'Your Header Line 1',
ch_line_2: 'Your Header Line 2',
ch_line_3: 'Your Header Line 3',
cf_line_1: 'Your Footer Line 1',
cf_line_2: 'Your Footer Line 2',
cf_line_3: 'Your Footer Line 3',
cl_operator: 'Operator Name',
cl_time: 'Time',
cl_trx_number: 'Transaction Number',
cl_customer_name: 'Customer Name',
cl_discount: 'Discount',
cl_total_discount: 'Total Discount',
cl_total: 'Total',
cl_tax: 'Tax',
cl_member: 'Member',
cl_paid: 'Paid',
cl_return: 'Return',
cl_debt: 'Debt',
cl_saving: 'Saving',
cl_due_date: 'Due date',
line_feed_each_in_items: 15,
column_width: 40,
more_new_line: 1,
pull_cash_drawer: 'no',
template: 'epson'
}
const interfaceOptions = [
{ value: 'cpus', key: 'CPUS' },
{ value: 'ethernet', key: 'Ethernet' },
{ value: 'linux-usb', key: 'Linux USB' },
{ value: 'smb', key: 'SMB' },
{ value: 'windows-usb', key: 'Windows USB' },
{ value: 'windows-lpt', key: 'Windows LPT' },
]
const fontTypeOptions = [
{ value: 'A', key: 'Font Type A' },
{ value: 'B', key: 'Font Type B' },
{ value: 'C', key: 'Font Type C' },
]
创建向用户提供输入设置的表单。您可以使用Formik或您知道要在React中创建表单的东西创建。
在我的(f)情况下,我使用Formik并使DEFAULT_PRINTER_SETTING
稳定为form initialValues
,然后使用interfaceOptions
和fontTypeOptions
以形式创建下拉选项。
创建表格提交处理程序
您可以在上面看到,形式需要提交以在Dexie存储中的存储设置。
// Filename: src/pages/PrinterSetting.js
import { db } from '../db'
import { DEFAULT_PRINTER_SETTING } from '../constants'
const formOnSubmit = async (values) => {
try {
db.printer_settings.count(count => {
if (count > 0) {
db.printer_settings.update(1, values).then(updated => {
if (updated)
console.log("Printer setting was updated!");
else
console.warn("Nothing was updated - there were no update changed");
});
} else {
db.printer_settings.add(DEFAULT_PRINTER_SETTING).then(() => {
console.log("New setting just created using default setting");
}).catch(error => {
console.error(error)
})
}
})
} catch (error) {
console.error(error)
}
}
创建打印机设置页面后,不要忘记喝咖啡并发射雪茄,播放下一首您讨厌大声的歌曲!
创建POS上下文
使生活变得轻松...在React中创建pos Context
和Provider
。将其放置并包装您的应用程序。
我们需要制作在整个应用中共享的WebSocket客户端:
- 共享Websocket连接状态
- 创建指标
- 将消息发送到WebSocket服务器
// Filename: src/contexts/POSContext.js
import useWebSocket from 'react-use-websocket'
import { db } from '../db'
import { DEFAULT_PRINTER_SETTING } from '../constants'
const POSContext = createContext()
const SOCKET_URL = 'ws://<your-websocket-address:port>'
const generateAsyncUrlGetter = (url, timeout = 2000) => () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(url);
}, timeout);
});
};
const POSProvider = ({ children }) => {
const [currentSocketUrl, setCurrentSocketUrl] = useState(SOCKET_URL)
const [printerSettings, setprinterSettings] = useState()
const { sendMessage, sendJsonMessage, readyState } = useWebSocket(
currentSocketUrl,
{
share: true,
shouldReconnect: () => true,
reconnectAttempts: 3,
reconnectInterval: 3000,
onError: (event) => {
alert.show('WebSocket trying to connect to sever but failed!', {
type: 'error',
position: 'bottom right',
timeout: 3000
})
},
onOpen: (event) => {
alert.show('WebSocket connection establised!', {
type: 'success',
position: 'bottom right',
timeout: 3000
})
},
onClose: (event) => {
alert.show('WebSocket connection is closed!', {
type: 'warning',
position: 'bottom right',
timeout: 3000
})
},
onReconnectStop: () => 6
}
)
const readyStateString = {
0: 'CONNECTING',
1: 'OPEN',
2: 'CLOSING',
3: 'CLOSED',
}[readyState]
const reconnectWebSocket = useCallback(() => {
if (readyState === 3) {
setCurrentSocketUrl(generateAsyncUrlGetter(SOCKET_URL))
} else {
setCurrentSocketUrl(null)
}
}, [setCurrentSocketUrl, readyState])
const initData = useCallback(async () => {
if (readyState === 1) {
db.printer_settings.count(count => {
if (count > 0) {
setprinterSettings(first(settings))
console.log('Setting is exists')
} else {
setprinterSettings(DEFAULT_PRINTER_SETTING)
console.log('Setting is not exists!')
}
})
} else {
console.log('Setting is not exists!')
setprinterSettings(DEFAULT_PRINTER_SETTING)
}
}, [readyState])
useEffect(() => {
if (readyState === 1) initData()
}, [readyState, initData])
return (
<KasirContext.Provider value={{
sendMessage,
sendJsonMessage,
reconnectWebSocket,
readyStateString,
printerSettings
}}>
{children}
</KasirContext.Provider>
)
}
export { KasirContext, KasirProvider }
喝咖啡和烟!更大的音量!
创建指示按钮
此按钮显示客户端和服务器之间的Websocket连接。此按钮还可以触发连接时重新连接到服务器。
// Filename: src/components/IndicatorButton.js
import React, { useContext } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { POSContext } from '../contexts/POSContext';
function IndicatorButton() {
const posCtx = useContext(POSContext)
return (
<button
className='btn btn-indicator'
onClick={() => posCtx.reconnectWebSocket()}
>
<FontAwesomeIcon icon="fa-solid fa-satellite-dish" />{' '}
Printer Server is:{' '}
<span
className={`${posCtx.readyStateString === 'CONNECTING' ? 'text-info' : (posCtx.readyStateString === 'OPEN') ? 'text-success' : (posCtx.readyStateString === 'CLOSED') ? 'text-secondary' : 'text-danger'}`}
>
{posCtx.readyStateString}
</span>
</button>
)
}
export default IndicatorButton
您可以将其放在代码中的某个地方。
现在! WebSocket客户端正在运行,但连接已关闭3次尝试将其重新连接到WebSocket服务器。
那么接下来呢?!
创建WebSocket服务器
使用workerman
创建WebSocket服务器就像魔术。
- 在
printer-server
目录中创建新的phpindex.php
文件 - Visit Workerman Repository
- 使用
npm
(愚蠢!)在printer-server
目录中安装workerman
和escpos-php
包装 - 复制和粘贴A simple websocket example
- 去您的巴士终端运行
php index.php start
就是这样,别无其他!而且您无法使用该代码进行任何打印呼叫。这很容易吧?
不要生气我!请开玩笑!
现在使用此代码更新WebSocket Server脚本!我向你保证...(我的头声音:$^u@%g@yt@#..... .....)
但是,在触摸WebSocket Server脚本之前,您需要知道 打印机服务器需要哪些数据?
上午印度尼西亚人,如果我的源代码包含任何与英语混合的印尼语言,请不要生气。您可以翻译印度尼西亚>使用翻译器的英语或之后,您可以学习更多印尼语言(Hahaha Just Kinding!接下来!)
有效载荷该Websocket服务器(需要打印机服务器)
{
"trx": {
"nama_kasir": "Umam",
"no_transaksi": "nomor-transaksi",
"trx": "only-number-trx",
"nama_pelanggan": "nama-pelanggan",
"tgl_transaksi": "tanggal-transaksi-format-yyyy-mm-dd",
"diskon": "jumlah-diskon",
"total_transaksi": "total-transaksi",
"uang_diterima": "uang-diterima",
"uang_kembali": "uang-kembali",
"jenis_pembayaran": "jenis-pembayaran",
"jatuh_tempo": "tanggal-jatuh-tempo",
"uang_titip": "jumlah-uang-titip",
"ekspedisi": null,
"ongkir": null,
"pajak": "0",
"potongan_member": "0"
},
"text_kembalian": 0,
"belanja": [
{
"barang_nama": "MIE SEDAP SOTO",
"harga": "0",
"jumlah": "50 Dus",
"sub_total": "5125000",
"initial_code": "BRG-605A5B07C3EE5",
"no_transaksi": "nomor-transaksi"
},
{
"barang_nama": "MIE SEDAP GORENG",
"harga": "107000",
"jumlah": "38 Dus",
"sub_total": "4066000",
"initial_code": "BRG-605A5B01530C4",
"no_transaksi": "nomor-transaksi"
},
{
"barang_nama": "PATI SG",
"harga": "0",
"jumlah": "100 Sak",
"sub_total": "25450000",
"initial_code": "BRG-625678D10EC80",
"no_transaksi": "nomor-transaksi"
}
],
"alamat_gudang": "Jl. AHMAD YANI - DESA SAMBIROTO - TAYU - PATI <br>\r\nDepan Masjid Baitussalam <br>\r\n0853 - 2622 - 5333 / 0856 - 4165 - 5202",
"app_name": "CV. Kalimasodo Agriculture",
"nama_lengkap": "Umam",
"from": "posclient",
"command": "posclient",
"printer_name": "EPSON TM-U220 Receipt",
"printer_settings": {
"printer_name": "/dev/usb/lp0",
"interface": "linux-usb",
"font_type": "A",
"custom_print_header": [
"Your Company Name",
"Your Subheading for Receipt",
"Your Third text on header"
],
"custom_print_footer": [
"Your Company Name",
"Your Subheading for Receipt",
"Your Third text on footer"
],
"custom_language": {
"operator": "Operator",
"time": "Time",
"trx_number": "TRX Number",
"costumer_name": "Costumer",
"discount": "Discount",
"total_discount": "Total Disc" ,
"total": "Total",
"tax": "Tax",
"member": "Member",
"paid": "Paid",
"return": "Return",
"loan": "Loan",
"saving": "Saving",
"tempo": "Tempo"
},
"line_feed_each_in_items": 1,
"column_width": 48,
"more_new_line": 1,
"pull_cash_drawer": false
}
}
您可以使用该模式按您的要求格式化该请求有效载荷。
创建WebSocket打印机服务器
以下是index.php
中的所有代码
<?php
use Workerman\Worker;
use Mike42\Escpos\Printer;
use Mike42\Escpos\PrintConnectors\DummyPrintConnector;
use Mike42\Escpos\PrintConnectors\CupsPrintConnector;
use Mike42\Escpos\PrintConnectors\NetworkPrintConnector;
use Mike42\Escpos\PrintConnectors\FilePrintConnector;
use Mike42\Escpos\PrintConnectors\WindowsPrintConnector;
use Mike42\Escpos\CapabilityProfile;
require_once __DIR__ . '/vendor/autoload.php';
// Create a Websocket server
$ws_worker = new Worker('websocket://0.0.0.0:2112');
// Emitted when new connection come
$ws_worker->onConnect = function ($connection) {
echo "New connection\n";
};
// Emitted when data received
$ws_worker->onMessage = function ($connection, $payload) {
// Send hello $data
$data = json_decode($payload, TRUE);
$options = $data['printer_settings'];
echo '> metadata: ' . $data['from'] . PHP_EOL;
sendingMessage($connection, createMessage('info', 'metadata: ' . $data['from']));
echo '> activer_printer: ' . $data['printer_name'] . PHP_EOL;
sendingMessage($connection, createMessage('info', 'active_printer: ' . $data['printer_name']));
echo '< echo', "\n";
print_r($options);
if ($data['from'] === 'posclient') {
print_reciept($data, $options);
} else if ($data['from'] === 'testprinter') {
}
};
// Emitted when connection closed
$ws_worker->onClose = function ($connection) {
echo "Connection closed\n";
};
// Run worker
Worker::runAll();
function print_reciept($arrData, $options = null)
{
// Printer Connector
$allowed_print_interfaces = ['cpus', 'ethernet', 'linux-usb', 'smb', 'windows-usb', 'windows-lpt'];
if (in_array($options['interface'], $allowed_print_interfaces)) {
switch ($options['interface']) {
case 'cpus':
$connector = new CupsPrintConnector($options['printer_name']);
break;
case 'ethernet':
$connector = new NetworkPrintConnector($options['printer_name'], 9100);
break;
case 'linux-usb':
$connector = new FilePrintConnector($options['printer_name']);
break;
case 'smb':
case 'windows-usb':
case 'windows-lpt':
default:
$connector = new WindowsPrintConnector($options['printer_name']);
break;
}
} else {
// Make sure you load a Star print connector or you may get gibberish.
$connector = new DummyPrintConnector();
$profile = CapabilityProfile::load("TSP600");
}
// Font Type
$selectPrinterFont = [
'A' => Printer::FONT_A,
'B' => Printer::FONT_B,
'C' => Printer::FONT_C
];
if ($options['template'] === 'epson') {
$fontSettings = [
'header_nota' => $selectPrinterFont['B'],
'sub_header_nota' => $selectPrinterFont['B'],
'detail_operator' => $selectPrinterFont['C'],
'detail_balanja' => $selectPrinterFont['C'],
'sub_promo' => $selectPrinterFont['B'],
'footer' => $selectPrinterFont['C'],
'sub_footer' => $selectPrinterFont['B']
];
} else {
$fontSettings = [
'header_nota' => $selectPrinterFont['C'],
'sub_header_nota' => $selectPrinterFont['C'],
'detail_operator' => $selectPrinterFont['C'],
'detail_balanja' => $selectPrinterFont['C'],
'sub_promo' => $selectPrinterFont['C'],
'footer' => $selectPrinterFont['C'],
'sub_footer' => $selectPrinterFont['C']
];
}
echo "Line feed each item: " . $options['line_feed_each_in_items'];
$printer = new Printer($connector);
$column_width = ($options['template'] === 'epson') ? 40 : 48;
$divider = str_repeat("-", $column_width) . "\n";
$optnl = ($options['template'] === 'epson') ? 0 : $options['more_new_line'];
$more_new_line = str_repeat("\n", $optnl);
// Membuat header nota
$printer->initialize();
$printer->selectPrintMode(Printer::MODE_DOUBLE_HEIGHT); // Setting teks menjadi lebih besar
$printer->setJustification(Printer::JUSTIFY_CENTER); // Setting teks menjadi rata tengah
$printer->setFont($fontSettings['header_nota']);
$printer->setEmphasis(true);
$printer->text($arrData['app_name'] . "\n");
$printer->setEmphasis(false);
$printer->feed(1);
// Membuat sub header nota
$printer->initialize();
$printer->setJustification(Printer::JUSTIFY_CENTER); // Setting teks menjadi rata tengah
$printer->setFont($fontSettings['sub_header_nota']);
$printer->setEmphasis(true);
if (count($options['custom_print_header']) > 0) {
for ($i = 0; $i < count($options['custom_print_header']); $i++) {
$header_text = str_replace('\r\n', "", $options['custom_print_header'][$i]);
$header_text = str_replace('<br>', PHP_EOL, $header_text);
$printer->text(strtoupper($header_text) . "\n");
}
} else {
$printer->text("MELAYANI SEPENUH HATI\n");
$printer->text(strtoupper(str_replace("<br>", "", $arrData['alamat_gudang'])) . "\n");
$printer->text("Buka Jam 07:30 - 16:30\n");
}
$printer->setEmphasis(false);
$printer->feed(1);
// Membuat detail operator
$printer->initialize();
$printer->setLineSpacing(20);
$printer->setFont($fontSettings['detail_operator']);
$printer->setEmphasis(true);
$printer->text($divider);
$printer->text(buatBarisSejajar($options['custom_language']['operator'] . " : ", $arrData['nama_lengkap'] . $more_new_line, $column_width));
$printer->text(buatBarisSejajar($options['custom_language']['time'] . " : ", $arrData['trx']['tgl_transaksi'] . ' ' . date('H:i:s') . $more_new_line, $column_width));
$printer->text(buatBarisSejajar($options['custom_language']['trx_number'] . " : ", $arrData['trx']['trx'] . $more_new_line, $column_width));
$printer->text(buatBarisSejajar($options['custom_language']['costumer_name'] . " : ", $arrData['trx']['nama_pelanggan'] . $more_new_line, $column_width));
$printer->text($divider);
$printer->setEmphasis(false);
$printer->feed(1);
// Print Detail Belanja
$printer->initialize();
$printer->setLineSpacing(20);
$printer->setFont($fontSettings['detail_balanja']);
$printer->setEmphasis(true);
$no = 1;
foreach ($arrData['belanja'] as $b) {
$printer->text(buatBarisContent($b['barang_nama'], idr_format($b['sub_total'], false), $options['template']));
$printer->text('@ ' . idr_format($b['harga'], true) . " x " . $b['jumlah'] . "\n");
$printer->feed($options['line_feed_each_in_items']);
$no++;
}
$printer->setEmphasis(false);
$printer->feed(1);
if (count($arrData['promo']) > 0) {
// Membuat sub promo
$printer->initialize();
$printer->setLineSpacing(20);
$printer->feed(1);
$printer->setJustification(Printer::JUSTIFY_CENTER); // Setting teks menjadi rata tengah
$printer->setFont($fontSettings['sub_promo']);
$printer->setEmphasis(true);
$printer->text($divider);
for ($i = 0; $i < count($arrData['promo']); $i++) {
$printer->text(wordwrap($arrData['promo'][$i], 35, "\n") . "\n\n");
}
$printer->setEmphasis(false);
$printer->feed(3);
}
// Print Footer
$printer->initialize();
$printer->setLineSpacing(20);
$printer->setFont($fontSettings['footer']);
$printer->setEmphasis(true);
$printer->text($divider);
// Total Belanja
if ($arrData['trx']['sebelum_diskon'] != $arrData['trx']['total_transaksi']) {
$printer->text(buatBarisSejajar('Total Belanja', idr_format($arrData['trx']['sebelum_diskon']), $column_width) . $more_new_line);
}
// Total Pajak
if ($arrData['trx']['pajak'] != 0) {
$printer->text(buatBarisSejajar($options['custom_language']['tax'], idr_format($arrData['trx']['pajak']), $column_width) . $more_new_line);
}
// Total Diskon
if ($arrData['trx']['diskon'] != 0) {
$printer->text(buatBarisSejajar($options['custom_language']['discount'], idr_format($arrData['trx']['diskon']), $column_width) . $more_new_line);
}
// Total Potongan Member
if ($arrData['trx']['potongan_member'] != 0) {
$printer->text(buatBarisSejajar($options['custom_language']['member'], $arrData['trx']['potongan_member'], $column_width) . $more_new_line);
}
$printer->text(buatBarisSejajar($options['custom_language']['total'], idr_format($arrData['trx']['total_transaksi']), $column_width) . $more_new_line);
// Total Bayar
$bayar = ($arrData['trx']['uang_diterima'] != 0) ? number_format($arrData['trx']['uang_diterima']) : '-';
$printer->text(buatBarisSejajar($options['custom_language']['paid'], $bayar, $column_width) . $more_new_line);
$text_kembalian = ($arrData['trx']['jenis_pembayaran'] != 'Non Tunai') ? number_format($arrData['trx']['uang_kembali']) : '-';
$printer->text(buatBarisSejajar($options['custom_language']['return'], $text_kembalian, $column_width) . $more_new_line);
// End Total Kembalian
if ($arrData['trx']['jenis_pembayaran'] == 'Non Tunai') {
$printer->text(buatBarisSejajar($options['custom_language']['tempo'], date('d/m/Y', strtotime($arrData['trx']['jatuh_tempo'])), $column_width) . $more_new_line);
$printer->text(buatBarisSejajar($options['custom_language']['saving'], number_format($arrData['trx']['uang_titip']), $column_width) . $more_new_line);
$printer->text(buatBarisSejajar($options['custom_language']['loan'], number_format($arrData['trx']['total_transaksi'] - $arrData['trx']['uang_titip']), $column_width) . $more_new_line);
}
$printer->setEmphasis(false);
$printer->feed(1);
// Membuat sub footer nota
$printer->initialize();
// $printer->setLineSpacing(20);
$printer->feed(1);
$printer->setJustification(Printer::JUSTIFY_CENTER); // Setting teks menjadi rata tengah
$printer->setFont($fontSettings['sub_footer']);
$printer->setEmphasis(true);
if (count($options['custom_print_footer']) > 0) {
$printer->text("Terimakasih sudah berbelanja di\n");
for ($i = 0; $i < count($options['custom_print_footer']); $i++) {
$printer->text($options['custom_print_footer'][$i] . "\n");
}
} else {
$printer->text("Terimakasih sudah berbelanja di\n");
$printer->text("CV. Kalimosodo Angriculture\n");
$printer->text("Barang yang sudah dibeli tidak bisa\n");
$printer->text("dikembalikan.\n");
}
$printer->setEmphasis(false);
$printer->feed(3);
$printer->cut();
// Get the data out as a string
// $data = $connector -> getData();
// echo $data . PHP_EOL;
/* Pulse */
if ($options['pull_cash_drawer']) {
$printer->pulse();
}
// Close the printer when done.
$printer->close();
}
function sendingMessage($connection, $message)
{
$connection->send($message);
}
function createMessage($type = 'info', $message)
{
$timestamps = date('Y/m/d H:i:s');
$log = strtoupper($type);
return "[$timestamps][$log] $message";
}
function buatBarisSejajar($kolom1, $kolom2, $x)
{
$divider = str_repeat("-", $x);
$full_width = strlen($divider);
$half_width = $full_width / 2;
$rawText = str_pad($kolom1, $half_width);
$rawText .= str_pad($kolom2, $half_width, " ", STR_PAD_LEFT);
return $rawText . "\n";
}
function buatBarisContent($kolom1, $kolom2, $template)
{
$paperLength = ($template === 'epson') ? 40 : 48;
$divider = str_repeat("-", $paperLength);
$full_width = strlen($divider);
$half_width = $full_width / 2;
$total_x1 = ($template === 'epson') ? 18 : 22;
$total_x2 = ($template === 'epson') ? 21 : 25;
$lebar_kolom_1 = $total_x1; // asale 18
$lebar_kolom_2 = $total_x2; // asale 21
// Melakukan wordwrap(), jadi jika karakter teks melebihi lebar kolom, ditambahkan \n
$kolom1 = wordwrap($kolom1, $lebar_kolom_1, "\n", true);
$kolom2 = wordwrap($kolom2, $lebar_kolom_2, "\n", true);
// Merubah hasil wordwrap menjadi array, kolom yang memiliki 2 index array berarti memiliki 2 baris (kena wordwrap)
$kolom1Array = explode("\n", $kolom1);
$kolom2Array = explode("\n", $kolom2);
// Mengambil jumlah baris terbanyak dari kolom-kolom untuk dijadikan titik akhir perulangan
$jmlBarisTerbanyak = max(count($kolom1Array), count($kolom2Array));
// Mendeklarasikan variabel untuk menampung kolom yang sudah di edit
$hasilBaris = array();
// Melakukan perulangan setiap baris (yang dibentuk wordwrap), untuk menggabungkan setiap kolom menjadi 1 baris
for ($i = 0; $i < $jmlBarisTerbanyak; $i++) {
// memberikan spasi di setiap cell berdasarkan lebar kolom yang ditentukan,
$hasilKolom1 = str_pad((isset($kolom1Array[$i]) ? $kolom1Array[$i] : ""), $lebar_kolom_1);
$hasilKolom2 = str_pad((isset($kolom2Array[$i]) ? $kolom2Array[$i] : ""), $lebar_kolom_2, " ", STR_PAD_LEFT);
// Menggabungkan kolom tersebut menjadi 1 baris dan ditampung ke variabel hasil (ada 1 spasi disetiap kolom)
$hasilBaris[] = $hasilKolom1 . " " . $hasilKolom2;
}
// Hasil yang berupa array, disatukan kembali menjadi string dan tambahkan \n disetiap barisnya.
return implode("\n", $hasilBaris) . "\n";
}
function idr_format($number, $prefix = false, $decimal_number = 0, $decimal_point = ',', $thousand_point = '.')
{
return ($prefix == false) ? number_format($number, $decimal_number, $decimal_point, $thousand_point) : 'Rp. ' . number_format($number, $decimal_number, $decimal_point, $thousand_point);
}
要运行上述代码,我们需要在命令行中使用以下命令,并使用此命令:
➜ php index.php start
Workerman[index.php] start in DEBUG mode
------------------------------------------- WORKERMAN --------------------------------------------
Workerman version:4.1.4 PHP version:7.4.33 Event-Loop:\Workerman\Events\Select
-------------------------------------------- WORKERS ---------------------------------------------
proto user worker listen processes status
tcp darkterminal none websocket://0.0.0.0:2112 1 [OK]
--------------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.
如果您想要更有效的东西,并且不要重复上述命令,请使用supervisord
,systemd
或nssm
(Windows用户的NSSM - the Non-Sucking Service Manager)
如果您发现此博客很有用,并向世界的另一面开放了主意,并想给我一杯咖啡或成为我的Github帐户上的赞助商:
对于赞助商/捐助者,您的名字将包含在即将发布的文章中,因此请确保从您的开发,Twitter,Github或您拥有的任何其他社交媒体帐户中提供您的用户名。