Files
OMS/app/entermembercenter/lib/stat.php
chenping 08e1c72cdd 【新增】抖店优先发货对接
【新增】抖店优质快递对接
【新增】抖店加价发顺丰对接
【新增】抖店顺丰包邮支持协商更换物流
2026-04-27 10:19:05 +08:00

252 lines
7.5 KiB
PHP

<?php
/**
* Copyright 2012-2026 ShopeX (https://www.shopex.cn)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class entermembercenter_stat
{
public function __construct($app = null)
{
}
/**
* 安装完成后上报一次(与 setup success / install_product 共用;哨兵幂等)
*
* @return void
*/
public function reportInstall()
{
$secret = defined('OPEN_SOURCE_STAT_SECRET_OMS') ? (string) OPEN_SOURCE_STAT_SECRET_OMS : '';
if ($secret === '') {
return;
}
$sentinel = $this->sentinelPath();
if ($sentinel && is_file($sentinel)) {
return;
}
$params = $this->buildParams();
$params['sign'] = $this->sign($params, $secret);
$body = json_encode($params, JSON_UNESCAPED_SLASHES);
if ($body === false) {
return;
}
$url = defined('OPEN_SOURCE_STAT_REPORT_URL') ? trim((string) OPEN_SOURCE_STAT_REPORT_URL) : '';
if ($url === '') {
return;
}
$timeout = defined('OPEN_SOURCE_STAT_HTTP_TIMEOUT') ? (int) OPEN_SOURCE_STAT_HTTP_TIMEOUT : 20;
if ($timeout < 1) {
$timeout = 20;
}
$ok = $this->postJson($url, $body, $timeout);
if ($ok && $sentinel) {
@file_put_contents($sentinel, (string) time(), LOCK_EX);
}
}
/**
* @param array $params payload without sign
* @param string $secret
* @return string uppercase md5
*/
public function sign(array $params, $secret)
{
unset($params['sign']);
ksort($params, SORT_STRING);
$s = '';
foreach ($params as $k => $v) {
if ($v === null) {
continue;
}
if (is_bool($v)) {
$v = $v ? 1 : 0;
}
$s .= $k . (is_array($v) ? json_encode($v, JSON_UNESCAPED_SLASHES) : $v);
}
return strtoupper(md5($secret . $s . $secret));
}
private function buildParams()
{
$product = defined('OPEN_SOURCE_STAT_PRODUCT') ? (string) OPEN_SOURCE_STAT_PRODUCT : 'oms';
return array(
'product' => $product,
'instance_id' => $this->detectInstanceId(),
'version' => $this->detectVersion(),
'timestamp' => time(),
'is_docker' => $this->detectDocker() ? 1 : 0,
'is_vm' => $this->detectVm() ? 1 : 0,
);
}
private function sentinelPath()
{
if (!defined('ROOT_DIR')) {
return null;
}
$dir = defined('DATA_DIR') ? DATA_DIR : (ROOT_DIR . '/data');
if (!is_dir($dir)) {
@mkdir($dir, 0775, true);
}
$name = defined('OPEN_SOURCE_STAT_SENTINEL_NAME') ? trim((string) OPEN_SOURCE_STAT_SENTINEL_NAME) : '.opensource_install_stat_reported';
if ($name === '' || strpos($name, '/') !== false || strpos($name, "\0") !== false) {
$name = '.opensource_install_stat_reported';
}
return $dir . '/' . $name;
}
private function detectVersion()
{
if (!class_exists('base_setup_config')) {
return '0.0.0';
}
try {
$info = base_setup_config::deploy_info();
if (!empty($info['ver'])) {
return (string) $info['ver'];
}
} catch (Exception $e) {
// ignore
}
return '0.0.0';
}
private function detectInstanceId()
{
if (PHP_OS_FAMILY === 'Linux' || stripos(PHP_OS, 'Linux') !== false) {
$glob = @glob('/sys/class/net/*/address');
if (is_array($glob)) {
foreach ($glob as $path) {
if (strpos($path, '/lo/') !== false) {
continue;
}
$raw = @trim((string) @file_get_contents($path));
if ($raw !== '' && strcasecmp($raw, '00:00:00:00:00:00') !== 0) {
return strtolower(str_replace('-', ':', $raw));
}
}
}
}
$host = function_exists('php_uname') ? php_uname('n') : 'unknown';
$root = defined('ROOT_DIR') ? ROOT_DIR : __DIR__;
return md5($host . '|' . $root);
}
private function detectDocker()
{
if (@is_file('/.dockerenv')) {
return true;
}
if (@is_readable('/proc/1/cgroup')) {
$c = @file_get_contents('/proc/1/cgroup');
if ($c !== false && strpos($c, 'docker') !== false) {
return true;
}
}
return false;
}
private function detectVm()
{
$paths = array(
'/sys/class/dmi/id/product_name',
'/sys/class/dmi/id/sys_vendor',
);
foreach ($paths as $p) {
if (!@is_readable($p)) {
continue;
}
$t = strtolower((string) @file_get_contents($p));
if ($t === '') {
continue;
}
if (preg_match('/vmware|virtualbox|kvm|qemu|xen|parallels|microsoft corporation|bochs|innotek/', $t)) {
return true;
}
}
return false;
}
/**
* @param string $url
* @param string $body
* @param int $timeoutSec
* @return bool true if HTTP 2xx
*/
private function postJson($url, $body, $timeoutSec)
{
if (function_exists('curl_init')) {
$ch = curl_init($url);
if ($ch === false) {
return false;
}
curl_setopt_array($ch, array(
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => array('Content-Type: application/json'),
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeoutSec,
CURLOPT_CONNECTTIMEOUT => min(10, $timeoutSec),
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
));
$resp = curl_exec($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $code >= 200 && $code < 300;
}
$ctx = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => "Content-Type: application/json\r\nConnection: close\r\n",
'content' => $body,
'timeout' => $timeoutSec,
'ignore_errors' => true,
),
'ssl' => array(
'verify_peer' => true,
'verify_peer_name' => true,
),
));
$resp = @file_get_contents($url, false, $ctx);
if ($resp === false) {
return false;
}
if (isset($http_response_header[0]) && preg_match('#\s(\d{3})\s#', $http_response_header[0], $m)) {
$code = (int) $m[1];
return $code >= 200 && $code < 300;
}
return false;
}
}