Merge branch '1.17.x' into develop

This commit is contained in:
JediKev
2025-02-04 09:46:36 -06:00
150 changed files with 2695 additions and 2106 deletions

2
.gitignore vendored
View File

@@ -7,6 +7,8 @@ setup/cli/modules/importer
.vagrant
._.DS_Store
Vagrantfile
# VS Code workspace files
*.code-workspace
# Staging directory used for packaging script
stage

View File

@@ -22,13 +22,13 @@ easy to setup and use. The best part is, it's completely free.
Requirements
------------
* HTTP server running Microsoft® IIS or Apache
* PHP version 8.1 - 8.2 (8.2 recommended)
* PHP version 8.2 - 8.4 (8.4 recommended)
* mysqli extension for PHP
* MySQL database version 5.5 (or greater)
### Recommendations
* fileinfo, gd, gettext, imap, intl, json, mbstring, Zend OPcache, phar,
xml, xml-dom, and zip extensions for PHP
* ctype, fileinfo, gd, gettext, iconv, imap, intl, json, mbstring,
Zend OPcache, phar, xml, xml-dom, and zip extensions for PHP
* APCu module enabled and configured for PHP
Deployment

View File

@@ -16,6 +16,67 @@ osTicket v1.18
* Database: Change Plugin Name to varchar(255) (aac546d0)
* update: MAJOR_VERSION (3eac42e5)
osTicket v1.17.6
================
### Improvements
* Fix PR #6283: DB column value of "local backend" is 'client' and not 'local' (7805cd94)
* i18n: Tasks Department ID Missing (00bc6b1b)
* issue: Checkbox Template Variable (09de9587)
* Fixed mailfetch error message (cd4717a1)
* issue: fix square characters being printed when printing tickets that's using languages like Thai (872a6492, 25844034)
* issue: Position Styling.. Again (7f03a64b, b843fb15)
* mysqli: Multi-Host Failure (0a8475fb)
* issue: Task Last Respondent (5277c7ab)
* i18n: Schedules getDays() (f2facda3)
* issue: Referred Closed Tickets (a7b0711d)
* php: Update Prerequisites (d331a44c)
* i18n: Lang URL Param (fece6ff7)
* mysql: Update Minimum Version (317ab967)
* Fix ajax path for AddRemoteCollaborator (6df6cd98)
* Fix staff mobile field class (f8455c82)
* patches: Latest 05/28/2024 (2d65cb77, 36b4c94f, 854cf47c, d9fe3ada)
* upgrade: Laminas-Mail (2.25.1) (c0a74162, 939a278c, f658268d)
* hotfix: Fix getRawEmail() (c26d840c, 1eb71197)
* issue: Linked Icon annotation (6c096cce)
* fix(email): fix case sensitivity in "Action" header retrieval (07843598)
* Upgrade mimeDecode.php from v1.48 to v1.5.6 2016-08-28 (cbd78ac8)
* mailer: sendmail() Failure (14e2057b)
* php: 8.3 Support (a3931f46, b38db372, fea5e1e0, 387a1c04, 136d372e)
* issue: Ticket Relations (9426b2dd)
* issue: Basic Search Selector (74b8bd0e)
* mail: Reapply Memory Optimization (eaaa64d9, ab76ffe8)
* php: iconv Recommendation (33ecc3a0)
* Ignore VS Code Workspace Files (0da2e0e0)
* issue: Email Remote Backend Name (b54dd584)
* issue: Safari Response Content Disposition (64a5df68)
* api: DueDate and Other Errors (14814ae2)
* issue: Multiselect List Export (f3bf8553)
* issue: Image Annotation (aeeb2850)
* user: List Import (fec70c4a)
* issue: Latest SQL Warnings (9bdfde5a)
* fields: Variable Name Validation (0724d1ad)
* issue: Transfer Empty Comments Var (ed87b257)
* update: Composer symfony/process (59f25918)
* issue: Client ACL Staff File Download (8255b2e7)
* update: PHP Deprecations & Warnings (84c14ace)
* issue: User Lookup Umlaut (504b0bfb)
* issue: Department Field User Import (5c2b6a5d)
* Fix force-https auto-enabling on settings page (3805bbfd)
* queue: getTotal Incorrect Counts (4f137dc2)
* issue: Referred Tickets Incorrect Queue Counts (b42cad6f)
* php: 8.4 Support (a4c0f674, b4938b99, 5e5a9ff5, fb9a39ba)
* issue: Canned Response Access (b930a68b)
* issue: Excessive Fetching Errors (c546a167)
* issue: Plain-text Base64 (216ded32)
* i18n: Crowdin API v2 (0ec6670d)
* issue: iFrame Logins (3a5da66b)
* issue: mimeDecode .eml Attachments (7fc3d8c2)
* patches: Latest 11/18/2024 (416b548b, 245e7554)
* Revert part of 0784359 commit (ec76a203)
### Security
* security: Latest Vulns 01/2025 (193f5fe0, ab6672fa)
osTicket v1.17.5
================
### Improvements

View File

@@ -22,8 +22,6 @@ class Bootstrap {
#Error reporting...Good idea to ENABLE error reporting to a file. i.e display_errors should be set to false
$error_reporting = E_ALL & ~E_NOTICE & ~E_WARNING;
if (defined('E_STRICT')) # 5.4.0
$error_reporting &= ~E_STRICT;
if (defined('E_DEPRECATED')) # 5.3.0
$error_reporting &= ~(E_DEPRECATED | E_USER_DEPRECATED);
error_reporting($error_reporting); //Respect whatever is set in php.ini (sysadmin knows better??)
@@ -214,15 +212,16 @@ class Bootstrap {
$hosts = explode(',', DBHOST);
foreach ($hosts as $host) {
$ferror = null;
if (!db_connect($host, DBUSER, DBPASS, $options)) {
$ferror = sprintf('Unable to connect to the database — %s',
db_connect_error());
}elseif(!db_select_database(DBNAME)) {
$ferror = sprintf('Unknown or invalid database: %s',
DBNAME);
}
// break if no error
if (!$ferror) break;
try {
if (!db_connect($host, DBUSER, DBPASS, $options))
$ferror = sprintf('Unable to connect to the database — %s', db_connect_error());
elseif (!db_select_database(DBNAME))
$ferror = sprintf('Unknown or invalid database: %s', DBNAME);
} catch (mysqli_sql_exception $e) {
$ferror = sprintf('Database error — %s', $e->getMessage());
}
// break if no error
if (!$ferror) break;
}
if ($ferror) //Fatal error
@@ -341,7 +340,7 @@ class Bootstrap {
static function croak($message) {
$msg = $message."\n\n".THISPAGE;
osTicket\Mail\Mailer::sendmail(ADMIN_EMAIL, 'osTicket Fatal Error', $msg,
sprintf('"osTicket Alerts"<%s>', ADMIN_EMAIL));
sprintf('"osTicket Alerts" <%s>', ADMIN_EMAIL));
//Display generic error to the user
Http::response(500, "<b>Fatal Error:</b> Contact system administrator.");
}

View File

@@ -20,6 +20,11 @@ require_once INCLUDE_DIR.'class.ajax.php';
class ContentAjaxAPI extends AjaxController {
function syslog($id) {
global $thisstaff;
// Ensure there is a Staff and that they are an Administrator
if (!$thisstaff || !$thisstaff->isAdmin())
Http::response(403, 'Access Denied');
if($id && ($log=Log::lookup($id))) {
$content=sprintf('<div
@@ -142,8 +147,8 @@ class ContentAjaxAPI extends AjaxController {
function manageContent($id, $lang=false) {
global $thisstaff, $cfg;
if (!$thisstaff)
Http::response(403, 'Login Required');
if (!$thisstaff || !$thisstaff->isAdmin())
Http::response(403, 'Access Denied');
$content = Page::lookup($id, $lang);
@@ -168,8 +173,8 @@ class ContentAjaxAPI extends AjaxController {
function manageNamedContent($type, $lang=false) {
global $thisstaff, $cfg;
if (!$thisstaff)
Http::response(403, 'Login Required');
if (!$thisstaff || !$thisstaff->isAdmin())
Http::response(403, 'Access Denied');
$langs = $cfg->getSecondaryLanguages();
@@ -182,8 +187,8 @@ class ContentAjaxAPI extends AjaxController {
function updateContent($id) {
global $thisstaff;
if (!$thisstaff)
Http::response(403, 'Login Required');
if (!$thisstaff || !$thisstaff->isAdmin())
Http::response(403, 'Access Denied');
elseif (!($content = Page::lookup($id)))
Http::response(404, 'No such content');

View File

@@ -242,6 +242,7 @@ class DraftAjaxAPI extends AjaxController {
$draft = Draft::create(array(
'namespace' => $namespace,
'body' => ''
));
if (!$draft->save())
Http::response(500, 'Unable to create draft');
@@ -312,7 +313,8 @@ class DraftAjaxAPI extends AjaxController {
Http::response(403, "Login required for image upload");
$draft = Draft::create(array(
'namespace' => $namespace
'namespace' => $namespace,
'body' => ''
));
if (!$draft->save())
Http::response(500, 'Unable to create draft');

View File

@@ -323,7 +323,7 @@ class UsersAjaxAPI extends AjaxController {
elseif (!$bk || !$id)
Http::response(422, 'Backend and user id required');
elseif (!($backend = AuthenticationBackend::getSearchDirectoryBackend($bk))
|| !($user_info = $backend->lookup($id)))
|| !($user_info = $backend->lookup(html_entity_decode($id))))
Http::response(404, 'User not found');
$form = UserForm::getUserForm()->getForm($user_info);

View File

@@ -43,15 +43,22 @@ class TicketApiController extends ApiController {
foreach ($form->getFields() as $field)
$supported[] = $field->get('name');
if(!strcasecmp($format, 'email')) {
$supported = array_merge($supported, array('header', 'mid',
'emailId', 'to-email-id', 'ticketId', 'reply-to', 'reply-to-name',
'in-reply-to', 'references', 'thread-type', 'system_emails',
'mailflags' => array('bounce', 'auto-reply', 'spam', 'viral'),
'recipients' => array('*' => array('name', 'email', 'source'))
));
$supported['attachments']['*'][] = 'cid';
switch ($format) {
case 'email':
$supported = array_merge($supported, [
'header', 'mid', 'emailId', 'to-email-id', 'ticketId', 'reply-to',
'reply-to-name', 'in-reply-to', 'references', 'thread-type', 'system_emails',
'mailflags' => ['bounce', 'auto-reply', 'spam', 'viral'],
'recipients' => ['*' => ['name', 'email', 'source']]
]);
$supported['attachments']['*'][] = 'cid';
break;
case 'json':
case 'xml':
$supported = array_merge($supported, [
'duedate', 'slaId', 'staffId'
]);
break;
}
return $supported;

View File

@@ -387,8 +387,8 @@ class ApiXmlDataParser extends XmlDataParser {
function fixup($current) {
global $cfg;
if($current['ticket'])
$current = $current['ticket'];
if (isset($current['ticket']))
$current = $current['ticket'];
if (!is_array($current))
return $current;

View File

@@ -192,7 +192,7 @@ extends InstrumentedList {
function getInlines($lang=false) { return $this->_getList(false, true, $lang); }
function getSeparates($lang=false) { return $this->_getList(true, false, $lang); }
function getAll($lang=false) { return $this->_getList(true, true, $lang); }
function count($lang=false) { return count($this->getSeparates($lang)); }
function count($lang=false): int { return count($this->getSeparates($lang)); }
function _getList($separates=false, $inlines=false, $lang=false) {
$base = $this;

View File

@@ -311,6 +311,12 @@ extends VerySimpleModel {
return true;
}
function staffCanAccess($staff) {
if (!$staff instanceof Staff)
return false;
return (!$this->dept || (($role = $staff->getRole($this->dept)) && $role->hasPerm(Canned::PERM_MANAGE)));
}
}
RolePermission::register( /* @trans */ 'Knowledgebase', Canned::getPermissions());

View File

@@ -195,7 +195,9 @@ extends VerySimpleModel {
))->delete();
}
static function getConfigsByNamespace(?string $namespace=null, $key, $value=false) {
static function getConfigsByNamespace(?string $namespace, $key, $value=false) {
$filter = array();
$filter['key'] = $key;

View File

@@ -914,7 +914,7 @@ class DynamicFormField extends VerySimpleModel {
/* `variable` is used for automation. Internally it's called `name` */
), "name");
}
if ($this->get('name') && !preg_match('/^(?!\d)([[:alnum:]]|_|-)+$/u', $this->get('name')))
if ($this->get('name') && !preg_match('/^(?!\d)([[:alnum:]]|_)+$/u', $this->get('name')))
$this->addError(__(
'Invalid character in variable name. Please use letters and numbers only.'
), 'name');
@@ -1355,7 +1355,7 @@ class DynamicFormEntry extends VerySimpleModel {
//use getChanges if getClean returns an empty array
$fieldClean = $field->getClean() ?: $field->getChanges();
if (is_array($fieldClean) && $fieldClean[0])
$fieldClean = json_decode($fieldClean[0], true);
$fieldClean = is_string($fieldClean[0]) ? json_decode($fieldClean[0], true) : $fieldClean[0];
} else
$fieldClean = $field->getClean();
@@ -1593,7 +1593,7 @@ class SelectionField extends FormField {
$selection = array();
if ($value && !is_array($value))
$value = array($value);
$value = JsonDataParser::parse($value) ?: array($value);
if ($value && is_array($value)) {
foreach ($value as $k=>$v) {
@@ -1633,6 +1633,7 @@ class SelectionField extends FormField {
$values = array();
$choices = $this->getChoices();
foreach (explode(',', $value) as $V) {
$V = trim($V);
if (isset($choices[$V]))
$values[$V] = $choices[$V];
}

View File

@@ -1357,7 +1357,7 @@ class SmtpAccount extends EmailAccount {
// matching.
if ($vars['smtp_active'] == 1
&& ($vars['smtp_auth_bk'] === 'mailbox')
&& (strpos($vars['auth_bk'], 'oauth2') === 0)
&& (strpos($vars['mailbox_auth_bk'], 'oauth2') === 0)
&& !$this->checkStrictMatching())
$_errors['smtp_auth_bk'] = sprintf('%s and %s', __('Resource Owner'), __('Email Mismatch'));

View File

@@ -322,7 +322,7 @@ static function departmentMembers($dept, $agents, $filename='', $how='csv') {
exit;
}
static function audits($type, ?string $filename=null, ?string $tableInfo=null, ?string $object=null, ?string $how='csv', ?bool $show_viewed=true, ?array $data=array(), CsvExporter $exporter) {
static function audits($type, ?string $filename, ?string $tableInfo, ?string $object, ?string $how='csv', ?bool $show_viewed=true, ?array $data=array(), CsvExporter $exporter) {
$headings = array('Description', 'Timestamp', 'IP');
switch ($type) {
case 'audit':
@@ -620,7 +620,7 @@ class CsvExporter extends Exporter {
}
function write($data) {
fputcsv($this->fp, $this->escape($data), $this->getDelimiter());
fputcsv($this->fp, $this->escape($data), $this->getDelimiter(), "\"", "");
}
}
@@ -725,7 +725,7 @@ class CsvResultsExporter extends ResultSetExporter {
$delimiter = $this->getDelimiter();
// Output a UTF-8 BOM (byte order mark)
fputs($this->output, chr(0xEF) . chr(0xBB) . chr(0xBF));
fputcsv($this->output, $this->getHeaders(), $delimiter);
fputcsv($this->output, $this->getHeaders(), $delimiter, "\"", "");
while ($row=$this->next())
fputcsv($this->output, array_map(
function($v){
@@ -733,7 +733,7 @@ class CsvResultsExporter extends ResultSetExporter {
return "'".$v;
return $v;
}, $row),
$delimiter);
$delimiter, "\"", "");
if (!$tmp)
fclose($this->output);

View File

@@ -89,7 +89,7 @@ class FilterAction extends VerySimpleModel {
return $this->_impl;
}
static function setFilterFlags(?object $actions=null, $flag, $bool) {
static function setFilterFlags(?object $actions, $flag, $bool) {
$flag = constant($flag);
if ($actions)
foreach ($actions as $action)

View File

@@ -335,9 +335,10 @@ class Format {
':<(a|span) (name|style)="(mso-bookmark\:)?_MailEndCompose">(.+)?<\/(a|span)>:', # Drop _MailEndCompose
':<div dir=(3D)?"ltr">(.*?)<\/div>(.*):is', # drop Gmail "ltr" attributes
':data-cid="[^"]*":', # drop image cid attributes
'(position:[^!";]+;?)',
'(position: ?(-webkit-)?(static|relative|fixed|absolute|sticky|initial|inherit);?)', # Position styling
':[\x{2002}-\x{200B}]+:u', # unicode spaces
),
array('', '', '', '', '<html', '$4', '$2 $3', '', ''),
array('', '', '', '', '<html', '$4', '$2 $3', '', '', ' '),
$html);
// HtmLawed specific config only
@@ -484,7 +485,7 @@ class Format {
$exclude = !$cfg->allowExternalImages();
$local = false;
$input = preg_replace_callback('/<img ([^>]*)(src="([^"]+)")([^>]*)\/?>/',
$input = preg_replace_callback('/<img ([^>]*?)(src="([^"]+)")([^>]*)\/?>/',
function($match) use ($local, $allowed, $exclude, $display) {
if (strpos($match[3], 'cid:') !== false)
$local = true;
@@ -536,7 +537,8 @@ class Format {
'/[\x{23E0}-\x{23EF}]/u', # More Buttons
'/[\x{2310}-\x{231F}]/u', # Hourglass/Watch
'/[\x{1000B6}]/u', # Private Use Area (Plane 16)
'/[\x{2322}-\x{232F}]/u' # Keyboard
'/[\x{2322}-\x{232F}]/u', # Keyboard
'/[\x{00B0}|\x{00A9}]/u' # Degrees/Copyright
), '', $text);
}

View File

@@ -1789,6 +1789,10 @@ class BooleanField extends FormField {
return ($value) ? __('Yes') : __('No');
}
function asVar($value, $id=false) {
return $this->toString($value);
}
function getClean($validate=true) {
if (!isset($this->_clean)) {
$this->_clean = (isset($this->value))
@@ -3332,7 +3336,7 @@ class DepartmentField extends ChoiceField {
function getValue() {
if (($value = parent::getValue()) && ($id=$this->getClean()))
return $value[$id];
return is_array($value) ? $value[$id] : $value;
}
function to_php($value, $id=false) {
@@ -6178,7 +6182,7 @@ class TransferForm extends Form {
break;
default:
throw new Exception(sprintf(__('%s: Unknown template style %s'),
get_class(), $options['template']));
get_class($this), $options['template']));
}
$form = $this;

View File

@@ -113,10 +113,6 @@ class Http {
if (false !== strpos($user_agent,'msie')
&& false !== strpos($user_agent,'win'))
return 'filename='.rawurlencode($filename);
elseif (false !== strpos($user_agent, 'safari')
&& false === strpos($user_agent, 'chrome'))
// Safari and Safari only can handle the filename as is
return 'filename='.str_replace(',', '', $filename);
else
// Use RFC5987
return "filename*=UTF-8''".rawurlencode($filename);

View File

@@ -251,7 +251,7 @@ class Internationalization {
static function isLanguageInstalled($code) {
$langs = self::availableLanguages();
return isset($langs[strtolower($code)]);
return isset($langs[strtolower((string) $code)]);
}
static function isLanguageEnabled($code) {

View File

@@ -63,7 +63,7 @@ class CsvImporter {
$named_fields[$f->get('name')] = $f;
// Read the first row and see if it is a header or not
if (!($data = fgetcsv($this->stream, 1000, ",")))
if (!($data = fgetcsv($this->stream, 1000, ",", "\"", "")))
throw new ImportError(__('Whoops. Perhaps you meant to send some CSV records'));
$headers = array();
@@ -170,7 +170,7 @@ implements Iterator {
function next() {
do {
if (($csv = fgetcsv($this->stream, 4096, ",")) === false) {
if (($csv = fgetcsv($this->stream, 4096, ",", "\"", "")) === false) {
// Read error
$this->current = false;
break;

View File

@@ -106,7 +106,7 @@ namespace osTicket\Mail {
$part = new MimePart($text);
$part->type = Mime::TYPE_TEXT;
$part->charset = $this->charset;
$part->encoding = $encoding ?: Mime::ENCODING_BASE64;
$part->encoding = $encoding;
$this->addMimeContent($part);
}
@@ -515,7 +515,7 @@ namespace osTicket\Mail {
*
*/
public function getRawEmail(int $i) {
return $this->getRawHeader($i) . $this->getRawContent($i);
return trim($this->getRawHeader($i)) . "\r\n\r\n" . $this->getRawContent($i);
}
/*

View File

@@ -77,7 +77,7 @@ class Mailer {
$this->from = $from;
elseif (\Validator::is_email($from)) {
$this->from = new \EmailAddress(
sprintf('"%s" <%s>', $name ?: '', $from));
str_contains($from, '<') ? $from : sprintf('"%s" <%s>', $name ?: '', $from));
} elseif (is_string($from))
$this->from = new \EmailAddress($from);
elseif (($email=$this->getEmail())) {

View File

@@ -259,7 +259,7 @@ class Fetcher {
$msg = sprintf("\n %s:\n",
_S('osTicket is having trouble fetching emails from the following mail account')).
"\n"._S('Email').": ".$mailbox->getEmail()->getAddress().
"\n"._S('Host Info').": ".$mailbox->getHostInfo();
"\n"._S('Host Info').": ".$mailbox->getHostInfo().
"\n"._S('Error').": ".$t->getMessage().
"\n\n ".sprintf(_S('%1$d consecutive errors. Maximum of %2$d allowed'),
$mailbox->getNumErrors(), $MAXERRORS).

View File

@@ -242,8 +242,13 @@ class Mail_Parse {
function getFromAddressList(){
return self::parseAddressList($this->getHeaderEntry('from'),
$this->charset);
if (!($header = $this->struct->headers['from']))
return null;
if (is_string($header) && (strpos($header, '<') !== false) && (strpos($header, '>') === false))
$header = $header.'>';
return Mail_Parse::parseAddressList($header, $this->charset);
}
function getDeliveredToAddressList() {
@@ -312,10 +317,10 @@ class Mail_Parse {
return false;
$info = self::splitHeaders($body);
if (!isset($info['Action']))
if (!isset($info['action']))
return false;
return strcasecmp($info['Action'], 'failed') === 0;
return strcasecmp($info['action'], 'failed') === 0;
}
function getDeliveryStatusMessage() {

View File

@@ -183,7 +183,7 @@ abstract class BaseMessageStorage implements MessageStorageBackend {
return $messages;
}
function getIterator() {
function getIterator(): Traversable {
$this->used = true;
$messages = $this->load();
if ($this->queued) {

View File

@@ -233,16 +233,16 @@ class ModelMeta implements ArrayAccess {
return $root;
}
function offsetGet($field) {
function offsetGet($field): mixed {
return $this->meta[$field];
}
function offsetSet($field, $what) {
function offsetSet($field, $what): void {
$this->meta[$field] = $what;
}
function offsetExists($field) {
function offsetExists($field): bool {
return isset($this->meta[$field]);
}
function offsetUnset($field) {
function offsetUnset($field): void {
throw new Exception('Model MetaData is immutable');
}
@@ -1368,7 +1368,7 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
return $list[0];
}
function count() {
function count(): int {
// Defer to the iterator if fetching already started
if (isset($this->_iterator)) {
return $this->_iterator->count();
@@ -1562,7 +1562,7 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
}
// IteratorAggregate interface
function getIterator($iterator=false) {
function getIterator($iterator=false): Traversable {
if (!isset($this->_iterator)) {
$class = $iterator ?: $this->getIteratorClass();
$it = new $class($this);
@@ -1593,16 +1593,16 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable, Countabl
}
// ArrayAccess interface
function offsetExists($offset) {
function offsetExists($offset): bool {
return $this->getIterator()->offsetExists($offset);
}
function offsetGet($offset) {
function offsetGet($offset): mixed {
return $this->getIterator()->offsetGet($offset);
}
function offsetUnset($a) {
function offsetUnset($a): void {
throw new Exception(__('QuerySet is read-only'));
}
function offsetSet($a, $b) {
function offsetSet($a, $b): void {
throw new Exception(__('QuerySet is read-only'));
}
@@ -1735,27 +1735,27 @@ implements ArrayAccess {
$this->inner->rewind();
}
function getIterator() {
function getIterator(): Traversable {
$this->asArray();
return new ArrayIterator($this->storage);
}
function offsetExists($offset) {
function offsetExists($offset): bool {
$this->fillTo($offset+1);
return count($this->storage) > $offset;
}
function offsetGet($offset) {
function offsetGet($offset): mixed {
$this->fillTo($offset+1);
return $this->storage[$offset];
}
function offsetUnset($a) {
function offsetUnset($a): void {
throw new Exception(__('QuerySet is read-only'));
}
function offsetSet($a, $b) {
function offsetSet($a, $b): void {
throw new Exception(__('QuerySet is read-only'));
}
function count($mode=COUNT_NORMAL) {
function count($mode=COUNT_NORMAL): int {
$this->asArray();
return count($this->storage);
}
@@ -1999,7 +1999,7 @@ implements IteratorAggregate {
return $model;
}
function getIterator() {
function getIterator(): Traversable {
$func = ($this->map) ? 'getRow' : 'getArray';
$func = array($this->resource, $func);
$cache = true;
@@ -2028,27 +2028,27 @@ implements Iterator {
$this->callback = $callback;
}
function rewind() {
function rewind(): void {
$this->eoi = false;
$this->next();
}
function key() {
function key(): mixed {
return $this->key;
}
function valid() {
function valid(): bool {
if (!isset($this->eoi))
$this->rewind();
return !$this->eoi;
}
function current() {
function current(): mixed {
if ($this->eoi) return false;
return $this->current;
}
function next() {
function next(): void {
try {
$cbk = $this->callback;
$this->current = $cbk();
@@ -2073,7 +2073,7 @@ implements IteratorAggregate {
$this->queryset = $queryset;
}
function getIterator() {
function getIterator(): Traversable {
$this->resource = $this->queryset->getQuery();
return new CallbackSimpleIterator(function() {
global $StopIteration;
@@ -2096,7 +2096,7 @@ implements IteratorAggregate {
$this->queryset = $queryset;
}
function getIterator() {
function getIterator(): Traversable {
$this->resource = $this->queryset->getQuery();
return new CallbackSimpleIterator(function() {
global $StopIteration;
@@ -2171,7 +2171,7 @@ extends ModelResultSet {
* Slight edit to the standard iteration method which will skip deleted
* items.
*/
function getIterator() {
function getIterator(): Traversable {
return new CallbackFilterIterator(parent::getIterator(),
function($i) { return !$i->__deleted__; }
);
@@ -2244,11 +2244,11 @@ extends ModelResultSet {
return clone $this->queryset;
}
function offsetUnset($a) {
function offsetUnset($a): void {
$this->fillTo($a);
$this->storage[$a]->delete();
}
function offsetSet($a, $b) {
function offsetSet($a, $b): void {
$this->fillTo($a);
if ($obj = $this->storage[$a])
$obj->delete();
@@ -2871,7 +2871,7 @@ class MySqlCompiler extends SqlCompiler {
$table = ($rmeta['view'])
// XXX: Support parameters from the nested query
? $rmodel::getSqlAddParams($this)
: $this->quote($rmeta['table']);
: $this->quote($rmeta['table'], true);
$base = "{$join}{$table} {$alias}";
return array($base, $constraints);
}

View File

@@ -320,7 +320,8 @@ class osTicket {
.',title='.db_input(Format::sanitize($title, true))
.',log_type='.db_input($loglevel[$level])
.',log='.db_input(Format::sanitize($message, false))
.',ip_address='.db_input($_SERVER['REMOTE_ADDR']);
.',ip_address='.db_input($_SERVER['REMOTE_ADDR'])
.',logger=""';
db_query($sql, false);

View File

@@ -27,9 +27,8 @@ class osTicketSession {
// session name/ssid
if ($name && strcmp($this->name, $name))
$this->name = $name;
// Session ttl cannot exceed php.ini maxlifetime setting
$maxlife = ini_get('session.gc_maxlifetime');
$this->ttl = min($ttl ?: ($maxlife ?: SESSION_TTL), $maxlife);
$this->ttl = $ttl ?: ($maxlife ?: SESSION_TTL);
// Set osTicket specific session name/sessid
session_name($this->name);
// Set Default cookie Params before we start the session
@@ -250,13 +249,19 @@ class osTicketSession {
}
static function renewCookie($baseTime=false, $window=false) {
global $ost;
$ttl = $window ?: SESSION_TTL;
$expire = ($baseTime ?: time()) + $ttl;
setcookie(session_name(), session_id(), $expire,
ini_get('session.cookie_path'),
ini_get('session.cookie_domain'),
ini_get('session.cookie_secure'),
ini_get('session.cookie_httponly'));
$opts = [
'expires' => $expire,
'path' => ini_get('session.cookie_path'),
'domain' => ini_get('session.cookie_domain'),
'secure' => ini_get('session.cookie_secure'),
'httponly' => ini_get('session.cookie_httponly'),
'samesite' => !empty($ost->getConfig()->getAllowIframes()) ? 'None' : 'Strict'
];
setcookie(session_name(), session_id(), $opts);
// Trigger expire update - neeed for secondary handlers that only
// log new sessions
self::expire(session_id(), $ttl);

View File

@@ -74,7 +74,7 @@ class Ticket2PDF extends mPDFWithLocalImages
$this->includenotes = $notes;
$this->includeevents = $events;
parent::__construct(['mode' => 'utf-8', 'format' => $psize, 'tempDir'=>sys_get_temp_dir()]);
parent::__construct(['mode' => 'utf-8', 'format' => $psize, 'tempDir'=>sys_get_temp_dir(), 'autoLangToFont' => true, 'autoScriptToLang' => true]);
$this->_print();
}
@@ -98,7 +98,7 @@ class Ticket2PDF extends mPDFWithLocalImages
return;
$html = ob_get_clean();
$this->autoScriptToLang;
$this->autoScriptToLang = true;
$this->WriteHtml($html, 0, true, true);
}
}
@@ -115,7 +115,7 @@ class Task2PDF extends mPDFWithLocalImages {
$this->task = $task;
$this->options = $options;
parent::__construct(['mode' => 'utf-8', 'format' => $this->options['psize'], 'tempDir'=>sys_get_temp_dir()]);
parent::__construct(['mode' => 'utf-8', 'format' => $this->options['psize'], 'tempDir'=>sys_get_temp_dir(), 'autoLangToFont' => true, 'autoScriptToLang' => true]);
$this->_print();
}
@@ -128,7 +128,7 @@ class Task2PDF extends mPDFWithLocalImages {
ob_start();
include STAFFINC_DIR.'templates/task-print.tmpl.php';
$html = ob_get_clean();
$this->autoScriptToLang;
$this->autoScriptToLang = true;
$this->WriteHtml($html, 0, true, true);
}

View File

@@ -1598,7 +1598,7 @@ abstract class QueueColumnAnnotation {
}
function getClassName() {
return @$this->config['c'] ?: get_class();
return @$this->config['c'] ?: get_class($this);
}
static function getAnnotations($root) {
@@ -1845,7 +1845,7 @@ extends QueueColumnAnnotation {
function getDecoration($row, $text) {
$flags = $row['flags'];
$linked = ($flags & Ticket::FLAG_LINKED) != 0;
if ($linked && $_REQUEST['a'] == 'search')
if ($linked)
return '<i class="icon-link"></i>';
}

View File

@@ -322,7 +322,7 @@ class RolePermission {
}
function getInfo() {
return $this->perms;
return $this->perms ?: [];
}
function merge($perms) {

View File

@@ -1049,13 +1049,15 @@ class ScheduleEntry extends VerySimpleModel {
}
static function getDays() {
static $translated = false;
if (!$translated) {
foreach (static::$days as $k=>$v)
static::$days[$k] = __($v);
}
return self::$days;
}
return static::$days;
static function getTranslatedDays() {
static $translated = null;
if (!isset($translated))
$translated = array_map(function ($d) { return __($d); }, static::getDays());
return $translated;
}
static function getWeeks() {
@@ -1176,7 +1178,7 @@ extends AbstractForm {
'default' => "",
'layout' => new GridFluidCell(6),
'label' => __('Day of the Week'),
'choices' => ScheduleEntry::getDays() + array(
'choices' => ScheduleEntry::getTranslatedDays() + array(
'weekdays' => sprintf('%s (%s)',
__('Weekdays'), __('Mon-Fri')),
'weekends' => sprintf('%s (%s)',
@@ -1212,7 +1214,7 @@ extends AbstractForm {
'default' => "",
'layout' => new GridFluidCell(6),
'label' => __('Day'),
'choices' => ScheduleEntry::getDays(),
'choices' => ScheduleEntry::getTranslatedDays(),
'validator-error' => __('Selection required'),
'visibility' => new VisibilityConstraint(
new Q(array('monthly__neq'=>'day')),
@@ -1241,7 +1243,7 @@ extends AbstractForm {
'default' => '',
'layout' => new GridFluidCell(4),
'label' => __('Day'),
'choices' => ScheduleEntry::getDays(),
'choices' => ScheduleEntry::getTranslatedDays(),
'validator-error' => __('Selection required'),
'configuration'=>array('prompt'=>__('Day')),
'visibility' => new VisibilityConstraint(

View File

@@ -371,8 +371,11 @@ class MysqlSearchBackend extends SearchBackend {
#elseif (count(explode(' ', $query)) == 1)
# $mode = ' WITH QUERY EXPANSION';
// Strip colon (:num) to avoid possible params injection
$query = preg_replace('/:(\d+)/i', '$1', $query);
// Sanitize query to avoid possible SQL injection via parameter markers
// This regex matches one or more colons followed by one or more digits,
// and then replaces the match with only the digits (i.e. stripping the colon(s)).
$query = preg_replace('/:+(\d+)/', '$1', $query);
// escape query and using it as search
$search = 'MATCH (Z1.title, Z1.content) AGAINST ('.db_input($query).$mode.')';
@@ -471,7 +474,7 @@ class MysqlSearchBackend extends SearchBackend {
* not indexed in the _search table and add it to the index.
*/
function IndexOldStuff() {
$class = get_class();
$class = get_class($this);
$auto_create = function($db_error) use ($class) {
if ($db_error != 1146)
@@ -905,7 +908,10 @@ class SavedQueue extends CustomQueue {
$query = $this->getQuery();
if ($agent)
$query = $agent->applyVisibility($query);
$query->limit(false)->offset(false)->order_by(false);
$query->filter(Q::any([
'ticket_pid__isnull' => true,
'flags__hasbit' => Ticket::FLAG_LINKED
]))->limit(false)->offset(false)->order_by(false);
try {
return $query->count();
} catch (Exception $e) {

View File

@@ -18,8 +18,8 @@ class SetupWizard {
//Mimimum requirements
static protected $prereq = array(
'php' => '8.0',
'mysql' => '5.0');
'php' => '8.1',
'mysql' => '5.5');
//Version info - same as the latest version.

View File

@@ -713,7 +713,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
'child_thread__referrals__team__team_id__in' => $teams)));
$assigned->add($childRefTeam);
}
$visibility = Q::any(new Q(array('status__state'=>'open', $assigned)));
$visibility = Q::any(new Q(array('status__state__in'=>['open', 'closed'], $assigned)));
// -- If access is limited to assigned only, return assigned
if ($this->isAccessLimited())
return $visibility;
@@ -738,7 +738,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
}
function applyVisibility($query, $exclude_archived=false) {
return $query->filter($this->getTicketsVisibility($exclude_archived));
return $query->filter($this->getTicketsVisibility($exclude_archived))->distinct('ticket_id');
}
function applyDeptVisibility($qs) {

View File

@@ -350,23 +350,21 @@ class Task extends TaskModel implements RestrictedAccess, Threadable {
}
function getLastRespondent() {
if (!isset($this->lastrespondent)) {
$this->lastrespondent = Staff::objects()
->filter(array(
'staff_id' => static::objects()
'staff_id' => $this->getThread()->entries
->filter(array(
'thread__entries__type' => 'R',
'thread__entries__staff_id__gt' => 0
'type' => 'R',
'staff_id__gt' => 0
))
->values_flat('thread__entries__staff_id')
->order_by('-thread__entries__id')
->limit('1,1')
->values_flat('staff_id')
->order_by('-id')
->limit('1')
))
->first()
?: false;
}
return $this->lastrespondent;
}

View File

@@ -1665,6 +1665,7 @@ implements TemplateVariable {
$entry = new static(array(
'created' => SqlFunction::NOW(),
'updated' => SqlFunction::NOW(),
'type' => $vars['type'],
'thread_id' => $vars['threadId'],
'title' => Format::strip_emoticons(Format::sanitize($vars['title'], true)),
@@ -2201,8 +2202,8 @@ class ThreadEvent extends VerySimpleModel {
$inst = self::create(array(
'thread_type' => ObjectModel::OBJECT_TYPE_TICKET,
'staff_id' => $staff,
'team_id' => $ticket->getTeamId(),
'staff_id' => $staff ?: 0,
'team_id' => $ticket->getTeamId() ?: 0,
'dept_id' => $ticket->getDeptId(),
'topic_id' => $ticket->getTopicId(),
), $user);
@@ -2212,8 +2213,8 @@ class ThreadEvent extends VerySimpleModel {
static function forTask($task, $state, $user=false) {
$inst = self::create(array(
'thread_type' => ObjectModel::OBJECT_TYPE_TASK,
'staff_id' => $task->getStaffId(),
'team_id' => $task->getTeamId(),
'staff_id' => $task->getStaffId() ?: 0,
'team_id' => $task->getTeamId() ?: 0,
'dept_id' => $task->getDeptId(),
), $user);
return $inst;

View File

@@ -715,7 +715,7 @@ implements RestrictedAccess, Threadable, Searchable {
if (!$lock->delete())
return false;
$this->lock = null;
$this->lock = 0;
return $this->save();
}
@@ -2725,7 +2725,7 @@ implements RestrictedAccess, Threadable, Searchable {
&& ($msg=$tpl->getTransferAlertMsgTemplate())
) {
$msg = $this->replaceVars($msg->asArray(),
array('comments' => $note, 'staff' => $thisstaff));
array('comments' => $note ?: '', 'staff' => $thisstaff));
// Recipients
$recipients = array();
// Assigned staff or team... if any
@@ -4370,7 +4370,7 @@ implements RestrictedAccess, Threadable, Searchable {
$ticket->email_id = $vars['emailId'];
//Make sure the origin is staff - avoid firebug hack!
if ($vars['duedate'] && !strcasecmp($origin,'staff'))
if ($vars['duedate'] && in_array(strtolower($origin), ['staff', 'api']))
$ticket->duedate = date('Y-m-d G:i',
Misc::dbtime($vars['duedate']));

View File

@@ -98,12 +98,12 @@ implements IteratorAggregate, Countable {
}
// IteratorAggregate
function getIterator() {
function getIterator(): Traversable {
return new ArrayIterator($this->storage);
}
// Countable
function count($mode=COUNT_NORMAL) {
function count($mode=COUNT_NORMAL): int {
return count($this->storage, $mode);
}
@@ -195,7 +195,7 @@ implements ArrayAccess, Serializable {
}
// ArrayAccess
function offsetGet($offset) {
function offsetGet($offset): mixed {
if (!is_int($offset))
throw new InvalidArgumentException('List indices should be integers');
elseif ($offset < 0)
@@ -204,10 +204,11 @@ implements ArrayAccess, Serializable {
throw new OutOfBoundsException('List index out of range');
return $this->storage[$offset];
}
function offsetSet($offset, $value) {
if ($offset === null)
return $this->storage[] = $value;
elseif (!is_int($offset))
function offsetSet($offset, $value): void {
if ($offset === null) {
$this->storage[] = $value;
return;
} elseif (!is_int($offset))
throw new InvalidArgumentException('List indices should be integers');
elseif ($offset < 0)
$offset += count($this->storage);
@@ -217,14 +218,14 @@ implements ArrayAccess, Serializable {
$this->storage[$offset] = $value;
}
function offsetExists($offset) {
function offsetExists($offset): bool {
if (!is_int($offset))
throw new InvalidArgumentException('List indices should be integers');
elseif ($offset < 0)
$offset += count($this->storage);
return isset($this->storage[$offset]);
}
function offsetUnset($offset) {
function offsetUnset($offset): void {
if (!is_int($offset))
throw new InvalidArgumentException('List indices should be integers');
elseif ($offset < 0)

View File

@@ -367,7 +367,8 @@ class Validator {
$aclbk = $cfg->getACLBackend();
switch($backend) {
case 'client':
if (in_array($aclbk, array(0,3)))
if (in_array($aclbk, array(0,3))
|| ($aclbk == 2 && StaffAuthenticationBackend::getUser()))
return true;
break;
case 'staff':

View File

@@ -91,14 +91,14 @@ class AgentManager extends Module {
if (!($this->stream = fopen($stream, 'c')))
$this->fail("Unable to open output file [{$options['file']}]");
fputcsv($this->stream, array('First Name', 'Last Name', 'Email', 'UserName'));
fputcsv($this->stream, array('First Name', 'Last Name', 'Email', 'UserName'), ",", "\"", "");
foreach ($this->getAgents($options) as $agent)
fputcsv($this->stream, array(
$agent->getFirstName(),
$agent->getLastName(),
$agent->getEmail(),
$agent->getUserName(),
));
), ",", "\"", "");
break;
case 'list':

View File

@@ -53,10 +53,10 @@ class i18n_Compiler extends Module {
profile in your home folder or in your environment. See AWS
configuration docs for more information";
static $project = 'osticket-official';
static $crowdin_api_url = 'http://i18n.osticket.com/api/project/{project}/{command}';
static $project_id = '25755';
static $crowdin_api_url = 'http://i18n.osticket.com/api/v2/projects/{project_id}/{command}';
function _http_get($url) {
function _http_get($url, $headers=[], $fields=[]) {
$this->stdout->write(">>> Downloading $url\n");
#curl post
$ch = curl_init();
@@ -65,6 +65,15 @@ class i18n_Compiler extends Module {
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$headers[] = 'Cache-Control: no-cache';
// Set POST data
if (!empty($fields)) {
$headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
}
// Set headers
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result=curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
@@ -72,18 +81,18 @@ class i18n_Compiler extends Module {
return array($code, $result);
}
function _request($command, $args=array()) {
$url = str_replace(array('{command}', '{project}'),
array($command, self::$project),
function _request($command, $args=array(), $fields=[]) {
$url = str_replace(array('{command}', '{project_id}'),
array($command, self::$project_id),
self::$crowdin_api_url);
$args += array('key' => $this->key);
if ($branch = $this->getOption('branch', false))
$args += array('branch' => $branch);
$url .= '?' . Http::build_query($args);
// Set Personal Access Token for every request
$headers = ['Authorization: Bearer '.$this->key];
// Build URL with arguments
if (!empty($args))
$url .= '?' . Http::build_query($args);
return $this->_http_get($url);
return $this->_http_get($url, $headers, $fields);
}
function run($args, $options) {
@@ -128,20 +137,23 @@ class i18n_Compiler extends Module {
function _list() {
error_reporting(E_ALL);
list($code, $body) = $this->_request('status');
$d = new DOMDocument();
$d->loadXML($body);
// Get languages progress
list($code, $body) = $this->_request('languages/progress', ['limit' => 300]);
// Fail if not successful
if ($code !== 200)
$this->fail("Request failed ({$code}): ".JsonDataParser::parse($body)['error']['message']."\n");
$xp = new DOMXpath($d);
foreach ($xp->query('//language') as $c) {
// Parse response to get data
$langs = JsonDataParser::parse($body)['data'];
foreach ($langs as $lang) {
$name = $code = '';
foreach ($c->childNodes as $n) {
switch (strtolower($n->nodeName)) {
foreach ($lang['data']['language'] as $n=>$v) {
switch (strtolower($n)) {
case 'name':
$name = $n->textContent;
$name = $v;
break;
case 'code':
$code = $n->textContent;
case 'id':
$code = $v;
break;
}
}
@@ -152,8 +164,34 @@ class i18n_Compiler extends Module {
}
function _build($lang, $options) {
list($code, $zip) = $this->_request("download/$lang.zip");
// Set target language
$fields = ['targetLanguageIds' => [$lang]];
// Set branch ID from branch name
if ($branch = $this->getOption('branch', false))
$fields['branchId'] = $this->getBranchId($branch);
// List available builds for language
list($code, $result) = $this->_request("translations/builds", [], $fields);
// Parse response and return build ID
// Fail if no build ID as it's required
if ($code !== 201 || !($build_id = JsonDataParser::parse($result)['data']['id']))
$this->fail("No builds available for target language");
$progress = 0;
while ($progress<100) {
sleep(2);
list ($c, $r) = $this->_request("translations/builds/{$build_id}");
if ($c !== 200)
$this->fail("No builds available for target language");
$progress = (int) JsonDataParser::parse($r)['data']['progress'];
}
// Request the download information
list($code, $body) = $this->_request("translations/builds/{$build_id}/download");
// Parse response and return download URL - fail if none
if (!($url = JsonDataParser::parse($body)['data']['url']))
$this->fail('Selected build not available for target language');
// Download using the URL in the response
list($code, $zip) = $this->_http_get($url);
// Fail if not successful
if ($code !== 200)
$this->fail('Language is not available'."\n");
@@ -181,10 +219,6 @@ class i18n_Compiler extends Module {
$branch = trim($options['branch'], '/') . '/';
for ($i=0; $i<$zip->numFiles; $i++) {
$info = $zip->statIndex($i);
if ($branch && strpos($info['name'], $branch) !== 0) {
// Skip files not part of the named branch
continue;
}
$contents = $zip->getFromIndex($i);
if (!$contents)
continue;
@@ -737,6 +771,13 @@ class i18n_Compiler extends Module {
}
return $strings;
}
function getBranchId($branch) {
list($code, $result) = $this->_request("branches", ['name' => (string) $branch]);
if ($code !== 200)
$this->fail('Branch not available');
return (int) JsonDataParser::parse($result)['data'][0]['data']['id'];
}
}
Module::register('i18n', 'i18n_Compiler');

View File

@@ -59,11 +59,11 @@ class ListManager extends Module {
if (!($this->stream = fopen($stream, 'c')))
$this->fail("Unable to open output file [{$options['file']}]");
fputcsv($this->stream, array('Value', 'Abbrev'));
fputcsv($this->stream, array('Value', 'Abbrev'), ",", "\"", "");
foreach ($list->getItems() as $item)
fputcsv($this->stream, array(
(string) $item->getValue(),
$item->getAbbrev()));
$item->getAbbrev()), ",", "\"", "");
break;
case 'show':
$lists = DynamicList::objects()->order_by('-type', 'name');

View File

@@ -33,7 +33,7 @@ class OrganizationManager extends Module {
$this->fail("Unable to open input file [{$options['file']}]");
//Read the header (if any)
if (($data = fgetcsv($this->stream, 1000, ","))) {
if (($data = fgetcsv($this->stream, 1000, ",", "\"", ""))) {
if (strcasecmp($data[0], 'name'))
fseek($this->stream, 0); // We don't have an header!
else;
@@ -41,7 +41,7 @@ class OrganizationManager extends Module {
// for now we're assuming one column of Name
}
while (($data = fgetcsv($this->stream, 1000, ",")) !== FALSE) {
while (($data = fgetcsv($this->stream, 1000, ",", "\"", "")) !== FALSE) {
if (!$data[0])
$this->stderr->write('Invalid data format: Name
required');
@@ -55,10 +55,10 @@ class OrganizationManager extends Module {
if (!($this->stream = fopen($stream, 'c')))
$this->fail("Unable to open output file [{$options['file']}]");
fputcsv($this->stream, array('Name'));
fputcsv($this->stream, array('Name'), ",", "\"", "");
foreach (Organization::objects() as $org)
fputcsv($this->stream,
array((string) $org->getName()));
array((string) $org->getName()), ",", "\"", "");
break;
default:
$this->stderr->write('Unknown action!');

View File

@@ -76,10 +76,10 @@ class UserManager extends Module {
if (!($this->stream = fopen($stream, 'c')))
$this->fail("Unable to open output file [{$options['file']}]");
fputcsv($this->stream, array('Name', 'Email'));
fputcsv($this->stream, array('Name', 'Email'), ",", "\"", "");
foreach (User::objects() as $user)
fputcsv($this->stream,
array((string) $user->getName(), $user->getEmail()));
array((string) $user->getName(), $user->getEmail()), ",", "\"", "");
break;
case 'activate':

View File

@@ -1020,14 +1020,16 @@ function mb_wordwrap($string, $width=75, $break="\n", $cut=false) {
return rtrim(preg_replace($search, $replace, $string), $break);
}
// Thanks http://www.php.net/manual/en/ref.mbstring.php#90611
function mb_str_pad($input, $pad_length, $pad_string=" ",
$pad_style=STR_PAD_RIGHT) {
$match = array();
$marks = preg_match_all('/\p{M}/u', $input, $match);
return str_pad($input,
strlen($input)-mb_strwidth($input)+$marks+$pad_length, $pad_string,
$pad_style);
if (!function_exists('mb_str_pad')) {
// Thanks http://www.php.net/manual/en/ref.mbstring.php#90611
function mb_str_pad($input, $pad_length, $pad_string=" ",
$pad_style=STR_PAD_RIGHT) {
$match = array();
$marks = preg_match_all('/\p{M}/u', $input, $match);
return str_pad($input,
strlen($input)-mb_strwidth($input)+$marks+$pad_length, $pad_string,
$pad_style);
}
}
// Enable use of html2text from command line

View File

@@ -3,19 +3,19 @@
[![Build Status](https://github.com/laminas/laminas-mail/workflows/Continuous%20Integration/badge.svg)](https://github.com/laminas/laminas-mail/actions?query=workflow%3A"Continuous+Integration")
> ## 🇷🇺 Русским гражданам
>
>
> Мы, участники Laminas, родились и живем в разных странах. У многих из нас есть друзья, родственники и коллеги как в России, так и в Украине. Некоторые из нас родились в России. Некоторые из нас живут в России. У некоторых бабушки и дедушки сражались с фашистами во Второй мировой войне. Здесь никто не поддерживает фашизм.
>
>
> У одного из нас есть украинская родственница, которая спаслась из дома вместе с сыном. Поезд задержался из-за бомбежки на дороге впереди. У нас есть друзья, которые прячутся в бомбоубежищах. Мы с тревогой ждем весточки от них после воздушных налетов, которые беспорядочно наносят удары и попадают по больницам, школам, детским садам и домам. Мы не берем это из каких-либо СМИ. Мы наблюдаем это напрямую.
>
>
> Вы доверяете нам достаточно, чтоб использовать наши программы, и мы просим вас довериться нам вновь. Мы нуждаемся в помощи. Выходите и протестуйте против этой бесполезной войны. Остановите кровопролитие. Скажите "Нет войне!"
>
>
> ## 🇺🇸 To Citizens of Russia
>
>
> We at Laminas come from all over the world. Many of us have friends, family and colleagues in both Russia and Ukraine. Some of us were born in Russia. Some of us currently live in Russia. Some have grandparents who fought Nazis in World War II. Nobody here supports fascism.
>
>
> One team member has a Ukrainian relative who fled her home with her son. The train was delayed due to bombing on the road ahead. We have friends who are hiding in bomb shelters. We anxiously follow up on them after the air raids, which indiscriminately fire at hospitals, schools, kindergartens and houses. We're not taking this from any media. These are our actual experiences.
>
>
> You trust us enough to use our software. We ask that you trust us to say the truth on this. We need your help. Go out and protest this unnecessary war. Stop the bloodshed. Say "stop the war!"
`Laminas\Mail` provides generalized functionality to compose and send both text and

View File

@@ -8,28 +8,26 @@
"homepage": "https://laminas.dev",
"license": "BSD-3-Clause",
"require": {
"php": "~8.1.0 || ~8.2.0",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"ext-iconv": "*",
"laminas/laminas-loader": "^2.9.0",
"laminas/laminas-mime": "^2.11.0",
"laminas/laminas-servicemanager": "^3.22",
"laminas/laminas-stdlib": "^3.17.0",
"laminas/laminas-validator": "^2.31.0",
"symfony/polyfill-intl-idn": "^1.27.0",
"symfony/polyfill-mbstring": "^1.27.0",
"webmozart/assert": "^1.11.0",
"symfony/polyfill-intl-idn": "^1.27.0"
"webmozart/assert": "^1.11.0"
},
"require-dev": {
"laminas/laminas-coding-standard": "~2.5.0",
"laminas/laminas-crypt": "^3.10.0",
"laminas/laminas-db": "^2.18",
"laminas/laminas-servicemanager": "^3.21",
"phpunit/phpunit": "^10.1.3",
"phpunit/phpunit": "^10.4.2",
"psalm/plugin-phpunit": "^0.18.4",
"symfony/process": "^6.2.10",
"vimeo/psalm": "^5.11"
"symfony/process": "^6.4",
"vimeo/psalm": "^5.15"
},
"suggest": {
"laminas/laminas-crypt": "^3.10 Crammd5 support in SMTP Auth",
"laminas/laminas-servicemanager": "^3.21 when using SMTP to deliver messages"
},
"config": {

File diff suppressed because it is too large Load Diff

View File

@@ -380,7 +380,7 @@ class Imap
/**
* escape one or more literals i.e. for sendRequest
*
* @param string|array $string the literal/-s
* @param string $string the literal/-s
* @return string|array escape literals, literals with newline ar returned
* as array('{size}', 'string');
*/

View File

@@ -2,23 +2,25 @@
namespace Laminas\Mail\Protocol\Smtp\Auth;
use Laminas\Crypt\Hmac;
use Laminas\Mail\Exception\InvalidArgumentException;
use Laminas\Mail\Protocol\Smtp;
use function array_replace_recursive;
use function base64_decode;
use function base64_encode;
use function hash_hmac;
use function is_array;
use function is_string;
/**
* Performs CRAM-MD5 authentication
*/
class Crammd5 extends Smtp
{
/** @var string */
/** @var non-empty-string|null */
protected $username;
/** @var string */
/** @var non-empty-string|null */
protected $password;
/**
@@ -32,23 +34,18 @@ class Crammd5 extends Smtp
public function __construct($host = '127.0.0.1', $port = null, $config = null)
{
// Did we receive a configuration array?
$config = $config ?? [];
$origConfig = $config;
if (is_array($host)) {
// Merge config array with principal array, if provided
if (is_array($config)) {
$config = array_replace_recursive($host, $config);
} else {
$config = $host;
}
$config = array_replace_recursive($host, $config);
}
if (is_array($config)) {
if (isset($config['username'])) {
$this->setUsername($config['username']);
}
if (isset($config['password'])) {
$this->setPassword($config['password']);
}
if (isset($config['username'])) {
$this->setUsername($config['username']);
}
if (isset($config['password'])) {
$this->setPassword($config['password']);
}
// Call parent with original arguments
@@ -75,7 +72,7 @@ class Crammd5 extends Smtp
/**
* Set value for username
*
* @param string $username
* @param non-empty-string $username
* @return Crammd5
*/
public function setUsername($username)
@@ -87,7 +84,7 @@ class Crammd5 extends Smtp
/**
* Get username
*
* @return string
* @return non-empty-string|null
*/
public function getUsername()
{
@@ -97,7 +94,7 @@ class Crammd5 extends Smtp
/**
* Set value for password
*
* @param string $password
* @param non-empty-string $password
* @return Crammd5
*/
public function setPassword($password)
@@ -109,7 +106,7 @@ class Crammd5 extends Smtp
/**
* Get password
*
* @return string
* @return non-empty-string|null
*/
public function getPassword()
{
@@ -119,13 +116,21 @@ class Crammd5 extends Smtp
/**
* Prepare CRAM-MD5 response to server's ticket
*
* @param string $key Challenge key (usually password)
* @param string $data Challenge data
* @param non-empty-string $key Challenge key (usually password)
* @param non-empty-string $data Challenge data
* @param int $block Length of blocks (deprecated; unused)
* @return string
*/
protected function hmacMd5($key, $data, $block = 64)
protected function hmacMd5($key, $data, /** @deprecated */ $block = 64)
{
return Hmac::compute($key, 'md5', $data);
if (! is_string($key) || $key === '') {
throw new InvalidArgumentException('CramMD5 authentication requires a non-empty password');
}
if (! is_string($data) || $data === '') {
throw new InvalidArgumentException('CramMD5 authentication requires a non-empty challenge');
}
return hash_hmac('md5', $data, $key, false);
}
}

View File

@@ -4,6 +4,9 @@ namespace Laminas\Mail\Transport;
use Laminas\Stdlib\AbstractOptions;
/**
* @extends AbstractOptions<string|list<string>>
*/
class Envelope extends AbstractOptions
{
/** @var string|null */

View File

@@ -16,6 +16,9 @@ use function sprintf;
use function sys_get_temp_dir;
use function time;
/**
* @extends AbstractOptions<string|callable>
*/
class FileOptions extends AbstractOptions
{
/** @var string Path to stored mail files */

View File

@@ -11,6 +11,9 @@ use function is_object;
use function is_string;
use function sprintf;
/**
* @extends AbstractOptions<string>
*/
class SmtpOptions extends AbstractOptions
{
/** @var string Local client hostname */

View File

@@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit95d8fec1a9c27a6c5867f886a4b2ae3e::getLoader();
return ComposerAutoloaderInit5839f4fd6b3a30999ab415e06868370f::getLoader();

View File

@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit95d8fec1a9c27a6c5867f886a4b2ae3e
class ComposerAutoloaderInit5839f4fd6b3a30999ab415e06868370f
{
private static $loader;
@@ -24,16 +24,16 @@ class ComposerAutoloaderInit95d8fec1a9c27a6c5867f886a4b2ae3e
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit95d8fec1a9c27a6c5867f886a4b2ae3e', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit5839f4fd6b3a30999ab415e06868370f', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit95d8fec1a9c27a6c5867f886a4b2ae3e', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit5839f4fd6b3a30999ab415e06868370f', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit95d8fec1a9c27a6c5867f886a4b2ae3e::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit5839f4fd6b3a30999ab415e06868370f::getInitializer($loader));
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInit95d8fec1a9c27a6c5867f886a4b2ae3e::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInit5839f4fd6b3a30999ab415e06868370f::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInit95d8fec1a9c27a6c5867f886a4b2ae3e
class ComposerStaticInit5839f4fd6b3a30999ab415e06868370f
{
public static $files = array (
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => __DIR__ . '/..' . '/laminas/laminas-servicemanager/src/autoload.php',
@@ -105,9 +105,9 @@ class ComposerStaticInit95d8fec1a9c27a6c5867f886a4b2ae3e
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit95d8fec1a9c27a6c5867f886a4b2ae3e::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit95d8fec1a9c27a6c5867f886a4b2ae3e::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit95d8fec1a9c27a6c5867f886a4b2ae3e::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit5839f4fd6b3a30999ab415e06868370f::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit5839f4fd6b3a30999ab415e06868370f::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit5839f4fd6b3a30999ab415e06868370f::$classMap;
}, null, ClassLoader::class);
}

View File

@@ -2,21 +2,21 @@
"packages": [
{
"name": "laminas/laminas-loader",
"version": "2.9.0",
"version_normalized": "2.9.0.0",
"version": "2.10.0",
"version_normalized": "2.10.0.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-loader.git",
"reference": "51ed9c3fa42d1098a9997571730c0cbf42d078d3"
"reference": "e6fe952304ef40ce45cd814751ab35d42afdad12"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-loader/zipball/51ed9c3fa42d1098a9997571730c0cbf42d078d3",
"reference": "51ed9c3fa42d1098a9997571730c0cbf42d078d3",
"url": "https://api.github.com/repos/laminas/laminas-loader/zipball/e6fe952304ef40ce45cd814751ab35d42afdad12",
"reference": "e6fe952304ef40ce45cd814751ab35d42afdad12",
"shasum": ""
},
"require": {
"php": "~8.0.0 || ~8.1.0 || ~8.2.0"
"php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
},
"conflict": {
"zendframework/zend-loader": "*"
@@ -25,7 +25,7 @@
"laminas/laminas-coding-standard": "~2.4.0",
"phpunit/phpunit": "~9.5.25"
},
"time": "2022-10-16T12:50:49+00:00",
"time": "2023-10-18T09:58:51+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -61,22 +61,22 @@
},
{
"name": "laminas/laminas-mime",
"version": "2.11.0",
"version_normalized": "2.11.0.0",
"version": "2.12.0",
"version_normalized": "2.12.0.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-mime.git",
"reference": "60ec04b755821c79c1987ce291b44e69f2c0831f"
"reference": "08cc544778829b7d68d27a097885bd6e7130135e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-mime/zipball/60ec04b755821c79c1987ce291b44e69f2c0831f",
"reference": "60ec04b755821c79c1987ce291b44e69f2c0831f",
"url": "https://api.github.com/repos/laminas/laminas-mime/zipball/08cc544778829b7d68d27a097885bd6e7130135e",
"reference": "08cc544778829b7d68d27a097885bd6e7130135e",
"shasum": ""
},
"require": {
"laminas/laminas-stdlib": "^2.7 || ^3.0",
"php": "~8.0.0 || ~8.1.0 || ~8.2.0"
"php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
},
"conflict": {
"zendframework/zend-mime": "*"
@@ -89,7 +89,7 @@
"suggest": {
"laminas/laminas-mail": "Laminas\\Mail component"
},
"time": "2022-10-18T08:38:15+00:00",
"time": "2023-11-02T16:47:19+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -125,22 +125,22 @@
},
{
"name": "laminas/laminas-servicemanager",
"version": "3.21.0",
"version_normalized": "3.21.0.0",
"version": "3.22.1",
"version_normalized": "3.22.1.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-servicemanager.git",
"reference": "625f2aa3bc6dd02688b2da5155b3a69870812bda"
"reference": "de98d297d4743956a0558a6d71616979ff779328"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/625f2aa3bc6dd02688b2da5155b3a69870812bda",
"reference": "625f2aa3bc6dd02688b2da5155b3a69870812bda",
"url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/de98d297d4743956a0558a6d71616979ff779328",
"reference": "de98d297d4743956a0558a6d71616979ff779328",
"shasum": ""
},
"require": {
"laminas/laminas-stdlib": "^3.17",
"php": "~8.1.0 || ~8.2.0",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"psr/container": "^1.0"
},
"conflict": {
@@ -161,17 +161,16 @@
"laminas/laminas-code": "^4.10.0",
"laminas/laminas-coding-standard": "~2.5.0",
"laminas/laminas-container-config-test": "^0.8",
"laminas/laminas-dependency-plugin": "^2.2",
"mikey179/vfsstream": "^1.6.11",
"phpbench/phpbench": "^1.2.9",
"phpunit/phpunit": "^10.0.17",
"phpunit/phpunit": "^10.4",
"psalm/plugin-phpunit": "^0.18.4",
"vimeo/psalm": "^5.8.0"
},
"suggest": {
"friendsofphp/proxy-manager-lts": "ProxyManager ^2.1.1 to handle lazy initialization of services"
},
"time": "2023-05-14T12:24:54+00:00",
"time": "2023-10-24T11:19:47+00:00",
"bin": [
"bin/generate-deps-for-config-factory",
"bin/generate-factory-for-class"
@@ -219,33 +218,33 @@
},
{
"name": "laminas/laminas-stdlib",
"version": "3.17.0",
"version_normalized": "3.17.0.0",
"version": "3.19.0",
"version_normalized": "3.19.0.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-stdlib.git",
"reference": "dd35c868075bad80b6718959740913e178eb4274"
"reference": "6a192dd0882b514e45506f533b833b623b78fff3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/dd35c868075bad80b6718959740913e178eb4274",
"reference": "dd35c868075bad80b6718959740913e178eb4274",
"url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/6a192dd0882b514e45506f533b833b623b78fff3",
"reference": "6a192dd0882b514e45506f533b833b623b78fff3",
"shasum": ""
},
"require": {
"php": "~8.1.0 || ~8.2.0"
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
},
"conflict": {
"zendframework/zend-stdlib": "*"
},
"require-dev": {
"laminas/laminas-coding-standard": "^2.5",
"phpbench/phpbench": "^1.2.9",
"phpunit/phpunit": "^10.0.16",
"phpbench/phpbench": "^1.2.15",
"phpunit/phpunit": "^10.5.8",
"psalm/plugin-phpunit": "^0.18.4",
"vimeo/psalm": "^5.8"
"vimeo/psalm": "^5.20.0"
},
"time": "2023-03-20T13:51:37+00:00",
"time": "2024-01-19T12:39:49+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -281,23 +280,23 @@
},
{
"name": "laminas/laminas-validator",
"version": "2.31.0",
"version_normalized": "2.31.0.0",
"version": "2.55.0",
"version_normalized": "2.55.0.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-validator.git",
"reference": "7dc274aa5afd5e23be0dbea13363e3d66ba5808b"
"reference": "dc3f2609d41b1e21bc24e3e147d7dd284e8a1556"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-validator/zipball/7dc274aa5afd5e23be0dbea13363e3d66ba5808b",
"reference": "7dc274aa5afd5e23be0dbea13363e3d66ba5808b",
"url": "https://api.github.com/repos/laminas/laminas-validator/zipball/dc3f2609d41b1e21bc24e3e147d7dd284e8a1556",
"reference": "dc3f2609d41b1e21bc24e3e147d7dd284e8a1556",
"shasum": ""
},
"require": {
"laminas/laminas-servicemanager": "^3.21.0",
"laminas/laminas-stdlib": "^3.13",
"php": "~8.1.0 || ~8.2.0",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"psr/http-message": "^1.0.1 || ^2.0.0"
},
"conflict": {
@@ -305,17 +304,16 @@
},
"require-dev": {
"laminas/laminas-coding-standard": "^2.5",
"laminas/laminas-db": "^2.18",
"laminas/laminas-filter": "^2.32",
"laminas/laminas-http": "^2.18",
"laminas/laminas-i18n": "^2.23",
"laminas/laminas-session": "^2.16",
"laminas/laminas-uri": "^2.10.0",
"phpunit/phpunit": "^10.1.3",
"psalm/plugin-phpunit": "^0.18.4",
"psr/http-client": "^1.0.2",
"psr/http-factory": "^1.0.2",
"vimeo/psalm": "^5.11"
"laminas/laminas-db": "^2.20",
"laminas/laminas-filter": "^2.35.2",
"laminas/laminas-i18n": "^2.26.0",
"laminas/laminas-session": "^2.20",
"laminas/laminas-uri": "^2.11.0",
"phpunit/phpunit": "^10.5.20",
"psalm/plugin-phpunit": "^0.19.0",
"psr/http-client": "^1.0.3",
"psr/http-factory": "^1.1.0",
"vimeo/psalm": "^5.24.0"
},
"suggest": {
"laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator",
@@ -327,7 +325,7 @@
"laminas/laminas-uri": "Laminas\\Uri component, required by the Uri and Sitemap\\Loc validators",
"psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators"
},
"time": "2023-05-19T09:42:26+00:00",
"time": "2024-06-12T15:00:19+00:00",
"type": "library",
"extra": {
"laminas": {
@@ -476,17 +474,17 @@
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.29.0",
"version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "639084e360537a19f9ee352433b84ce831f3d2da"
"reference": "a287ed7475f85bf6f61890146edbc932c0fff919"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da",
"reference": "639084e360537a19f9ee352433b84ce831f3d2da",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919",
"reference": "a287ed7475f85bf6f61890146edbc932c0fff919",
"shasum": ""
},
"require": {
@@ -497,12 +495,9 @@
"suggest": {
"ext-intl": "For best performance"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -546,7 +541,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0"
},
"funding": [
{
@@ -566,17 +561,17 @@
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.29.0",
"version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
"reference": "bc45c394692b948b4d383a08d7753968bed9a83d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d",
"reference": "bc45c394692b948b4d383a08d7753968bed9a83d",
"shasum": ""
},
"require": {
@@ -585,12 +580,9 @@
"suggest": {
"ext-intl": "For best performance"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -633,7 +625,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0"
},
"funding": [
{
@@ -653,17 +645,17 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.29.0",
"version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"shasum": ""
},
"require": {
@@ -675,12 +667,9 @@
"suggest": {
"ext-mbstring": "For best performance"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -719,7 +708,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
},
"funding": [
{
@@ -739,28 +728,25 @@
},
{
"name": "symfony/polyfill-php72",
"version": "v1.27.0",
"version_normalized": "1.27.0.0",
"version": "v1.29.0",
"version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
"reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/861391a8da9a04cbad2d232ddd9e4893220d6e25",
"reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2022-11-03T14:55:06+00:00",
"time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -798,7 +784,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php72/tree/v1.29.0"
},
"funding": [
{

View File

@@ -17,9 +17,9 @@
),
),
'laminas/laminas-loader' => array(
'pretty_version' => '2.9.0',
'version' => '2.9.0.0',
'reference' => '51ed9c3fa42d1098a9997571730c0cbf42d078d3',
'pretty_version' => '2.10.0',
'version' => '2.10.0.0',
'reference' => 'e6fe952304ef40ce45cd814751ab35d42afdad12',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-loader',
'aliases' => array(),
@@ -35,36 +35,36 @@
'dev_requirement' => false,
),
'laminas/laminas-mime' => array(
'pretty_version' => '2.11.0',
'version' => '2.11.0.0',
'reference' => '60ec04b755821c79c1987ce291b44e69f2c0831f',
'pretty_version' => '2.12.0',
'version' => '2.12.0.0',
'reference' => '08cc544778829b7d68d27a097885bd6e7130135e',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-mime',
'aliases' => array(),
'dev_requirement' => false,
),
'laminas/laminas-servicemanager' => array(
'pretty_version' => '3.21.0',
'version' => '3.21.0.0',
'reference' => '625f2aa3bc6dd02688b2da5155b3a69870812bda',
'pretty_version' => '3.22.1',
'version' => '3.22.1.0',
'reference' => 'de98d297d4743956a0558a6d71616979ff779328',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-servicemanager',
'aliases' => array(),
'dev_requirement' => false,
),
'laminas/laminas-stdlib' => array(
'pretty_version' => '3.17.0',
'version' => '3.17.0.0',
'reference' => 'dd35c868075bad80b6718959740913e178eb4274',
'pretty_version' => '3.19.0',
'version' => '3.19.0.0',
'reference' => '6a192dd0882b514e45506f533b833b623b78fff3',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-stdlib',
'aliases' => array(),
'dev_requirement' => false,
),
'laminas/laminas-validator' => array(
'pretty_version' => '2.31.0',
'version' => '2.31.0.0',
'reference' => '7dc274aa5afd5e23be0dbea13363e3d66ba5808b',
'pretty_version' => '2.55.0',
'version' => '2.55.0.0',
'reference' => 'dc3f2609d41b1e21bc24e3e147d7dd284e8a1556',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-validator',
'aliases' => array(),
@@ -95,36 +95,36 @@
'dev_requirement' => false,
),
'symfony/polyfill-intl-idn' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '639084e360537a19f9ee352433b84ce831f3d2da',
'pretty_version' => 'v1.29.0',
'version' => '1.29.0.0',
'reference' => 'a287ed7475f85bf6f61890146edbc932c0fff919',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '19bd1e4fcd5b91116f14d8533c57831ed00571b6',
'pretty_version' => 'v1.29.0',
'version' => '1.29.0.0',
'reference' => 'bc45c394692b948b4d383a08d7753968bed9a83d',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534',
'pretty_version' => 'v1.29.0',
'version' => '1.29.0.0',
'reference' => '9773676c8a1bb1f8d4340a62efe641cf76eda7ec',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php72' => array(
'pretty_version' => 'v1.27.0',
'version' => '1.27.0.0',
'reference' => '869329b1e9894268a8a61dabb69153029b7a8c97',
'pretty_version' => 'v1.29.0',
'version' => '1.29.0.0',
'reference' => '861391a8da9a04cbad2d232ddd9e4893220d6e25',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php72',
'aliases' => array(),

View File

@@ -22,7 +22,7 @@
}
},
"require": {
"php": "~8.0.0 || ~8.1.0 || ~8.2.0"
"php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
},
"require-dev": {
"laminas/laminas-coding-standard": "~2.4.0",

View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "66f555baf92bb6bda8b28730fcceb30e",
"content-hash": "d00ced3d7abc2f3094a3205a51ef6376",
"packages": [],
"packages-dev": [
{
@@ -2091,8 +2091,8 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "~8.0.0 || ~8.1.0 || ~8.2.0"
"php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
},
"platform-dev": [],
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.6.0"
}

View File

@@ -1,22 +1,21 @@
# laminas-mime
> ## 🇷🇺 Русским гражданам
>
>
> Мы, участники Laminas, родились и живем в разных странах. У многих из нас есть друзья, родственники и коллеги как в России, так и в Украине. Некоторые из нас родились в России. Некоторые из нас живут в России. У некоторых бабушки и дедушки сражались с фашистами во Второй мировой войне. Здесь никто не поддерживает фашизм.
>
>
> У одного из нас есть украинская родственница, которая спаслась из дома вместе с сыном. Поезд задержался из-за бомбежки на дороге впереди. У нас есть друзья, которые прячутся в бомбоубежищах. Мы с тревогой ждем весточки от них после воздушных налетов, которые беспорядочно наносят удары и попадают по больницам, школам, детским садам и домам. Мы не берем это из каких-либо СМИ. Мы наблюдаем это напрямую.
>
>
> Вы доверяете нам достаточно, чтоб использовать наши программы, и мы просим вас довериться нам вновь. Мы нуждаемся в помощи. Выходите и протестуйте против этой бесполезной войны. Остановите кровопролитие. Скажите "Нет войне!"
>
>
> ## 🇺🇸 To Citizens of Russia
>
>
> We at Laminas come from all over the world. Many of us have friends, family and colleagues in both Russia and Ukraine. Some of us were born in Russia. Some of us currently live in Russia. Some have grandparents who fought Nazis in World War II. Nobody here supports fascism.
>
>
> One team member has a Ukrainian relative who fled her home with her son. The train was delayed due to bombing on the road ahead. We have friends who are hiding in bomb shelters. We anxiously follow up on them after the air raids, which indiscriminately fire at hospitals, schools, kindergartens and houses. We're not taking this from any media. These are our actual experiences.
>
>
> You trust us enough to use our software. We ask that you trust us to say the truth on this. We need your help. Go out and protest this unnecessary war. Stop the bloodshed. Say "stop the war!"
> This package is considered feature-complete, and is now in **security-only** maintenance mode, following a [decision by the Technical Steering Committee](https://github.com/laminas/technical-steering-committee/blob/2b55453e172a1b8c9c4c212be7cf7e7a58b9352c/meetings/minutes/2020-08-03-TSC-Minutes.md#vote-on-components-to-mark-as-security-only).
> If you have a security issue, please [follow our security reporting guidelines](https://getlaminas.org/security/).
> If you wish to take on the role of maintainer, please [nominate yourself](https://github.com/laminas/technical-steering-committee/issues/new?assignees=&labels=Nomination&template=Maintainer_Nomination.md&title=%5BNOMINATION%5D%5BMAINTAINER%5D%3A+%7Bname+of+person+being+nominated%7D)

View File

@@ -22,7 +22,7 @@
}
},
"require": {
"php": "~8.0.0 || ~8.1.0 || ~8.2.0",
"php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
"laminas/laminas-stdlib": "^2.7 || ^3.0"
},
"require-dev": {

View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "00a80a7fc74e8f70b9c97ea3791a1d84",
"content-hash": "0bb344763a1f69884ddce40e96a68855",
"packages": [
{
"name": "laminas/laminas-stdlib",
@@ -2895,8 +2895,8 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "~8.0.0 || ~8.1.0 || ~8.2.0"
"php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
},
"platform-dev": [],
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.6.0"
}

View File

@@ -4,19 +4,19 @@
[![Psalm coverage](https://shepherd.dev/github/laminas/laminas-servicemanager/coverage.svg?)](https://shepherd.dev/github/laminas/laminas-servicemanager)
> ## 🇷🇺 Русским гражданам
>
>
> Мы, участники Laminas, родились и живем в разных странах. У многих из нас есть друзья, родственники и коллеги как в России, так и в Украине. Некоторые из нас родились в России. Некоторые из нас живут в России. У некоторых бабушки и дедушки сражались с фашистами во Второй мировой войне. Здесь никто не поддерживает фашизм.
>
>
> У одного из нас есть украинская родственница, которая спаслась из дома вместе с сыном. Поезд задержался из-за бомбежки на дороге впереди. У нас есть друзья, которые прячутся в бомбоубежищах. Мы с тревогой ждем весточки от них после воздушных налетов, которые беспорядочно наносят удары и попадают по больницам, школам, детским садам и домам. Мы не берем это из каких-либо СМИ. Мы наблюдаем это напрямую.
>
>
> Вы доверяете нам достаточно, чтоб использовать наши программы, и мы просим вас довериться нам вновь. Мы нуждаемся в помощи. Выходите и протестуйте против этой бесполезной войны. Остановите кровопролитие. Скажите "Нет войне!"
>
>
> ## 🇺🇸 To Citizens of Russia
>
>
> We at Laminas come from all over the world. Many of us have friends, family and colleagues in both Russia and Ukraine. Some of us were born in Russia. Some of us currently live in Russia. Some have grandparents who fought Nazis in World War II. Nobody here supports fascism.
>
>
> One team member has a Ukrainian relative who fled her home with her son. The train was delayed due to bombing on the road ahead. We have friends who are hiding in bomb shelters. We anxiously follow up on them after the air raids, which indiscriminately fire at hospitals, schools, kindergartens and houses. We're not taking this from any media. These are our actual experiences.
>
>
> You trust us enough to use our software. We ask that you trust us to say the truth on this. We need your help. Go out and protest this unnecessary war. Stop the bloodshed. Say "stop the war!"
The Service Locator design pattern is implemented by the `Laminas\ServiceManager`

View File

@@ -21,7 +21,7 @@
"rss": "https://github.com/laminas/laminas-servicemanager/releases.atom"
},
"require": {
"php": "~8.1.0 || ~8.2.0",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"laminas/laminas-stdlib": "^3.17",
"psr/container": "^1.0"
},
@@ -31,10 +31,9 @@
"laminas/laminas-code": "^4.10.0",
"laminas/laminas-coding-standard": "~2.5.0",
"laminas/laminas-container-config-test": "^0.8",
"laminas/laminas-dependency-plugin": "^2.2",
"mikey179/vfsstream": "^1.6.11",
"phpbench/phpbench": "^1.2.9",
"phpunit/phpunit": "^10.0.17",
"phpunit/phpunit": "^10.4",
"psalm/plugin-phpunit": "^0.18.4",
"vimeo/psalm": "^5.8.0"
},
@@ -77,8 +76,7 @@
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"composer/package-versions-deprecated": true,
"laminas/laminas-dependency-plugin": true
"composer/package-versions-deprecated": true
},
"platform": {
"php": "8.1.99"

View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "bcc74076fd03d8f52e6d9320feeaf24e",
"content-hash": "9ec9c9a8b4746d56a1f1cc10cc26afbb",
"packages": [
{
"name": "laminas/laminas-stdlib",
@@ -1267,59 +1267,6 @@
],
"time": "2022-11-16T00:42:21+00:00"
},
{
"name": "laminas/laminas-dependency-plugin",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-dependency-plugin.git",
"reference": "73cfb63ddca9d6bfedad5e0a038f6d55063975a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-dependency-plugin/zipball/73cfb63ddca9d6bfedad5e0a038f6d55063975a3",
"reference": "73cfb63ddca9d6bfedad5e0a038f6d55063975a3",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.1 || ^2.0",
"php": "^7.3 || ~8.0.0 || ~8.1.0"
},
"require-dev": {
"composer/composer": "^1.9 || ^2.0",
"laminas/laminas-coding-standard": "^2.2.1",
"mikey179/vfsstream": "^1.6.10@alpha",
"phpunit/phpunit": "^9.5.5",
"psalm/plugin-phpunit": "^0.15.1",
"roave/security-advisories": "dev-master",
"vimeo/psalm": "^4.5"
},
"type": "composer-plugin",
"extra": {
"class": "Laminas\\DependencyPlugin\\DependencyRewriterPluginDelegator"
},
"autoload": {
"psr-4": {
"Laminas\\DependencyPlugin\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Replace zendframework and zfcampus packages with their Laminas Project equivalents.",
"support": {
"issues": "https://github.com/laminas/laminas-dependency-plugin/issues",
"source": "https://github.com/laminas/laminas-dependency-plugin/tree/2.2.0"
},
"funding": [
{
"url": "https://funding.communitybridge.org/projects/laminas-project",
"type": "community_bridge"
}
],
"time": "2021-09-08T17:51:35+00:00"
},
{
"name": "mikey179/vfsstream",
"version": "v1.6.11",
@@ -1483,16 +1430,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.15.4",
"version": "v4.17.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
"shasum": ""
},
"require": {
@@ -1533,9 +1480,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
},
"time": "2023-03-05T19:49:14+00:00"
"time": "2023-08-13T19:53:39+00:00"
},
{
"name": "phar-io/manifest",
@@ -2051,16 +1998,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "10.1.0",
"version": "10.1.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "fc4f5ee614fa82d50ecf9014b51af0a9561f3df8"
"reference": "355324ca4980b8916c18b9db29f3ef484078f26e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fc4f5ee614fa82d50ecf9014b51af0a9561f3df8",
"reference": "fc4f5ee614fa82d50ecf9014b51af0a9561f3df8",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e",
"reference": "355324ca4980b8916c18b9db29f3ef484078f26e",
"shasum": ""
},
"require": {
@@ -2079,7 +2026,7 @@
"theseer/tokenizer": "^1.2.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0"
"phpunit/phpunit": "^10.1"
},
"suggest": {
"ext-pcov": "PHP extension that provides line coverage",
@@ -2117,7 +2064,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.0"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7"
},
"funding": [
{
@@ -2125,20 +2072,20 @@
"type": "github"
}
],
"time": "2023-04-13T07:08:27+00:00"
"time": "2023-10-04T15:34:17+00:00"
},
{
"name": "phpunit/php-file-iterator",
"version": "4.0.1",
"version": "4.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd"
"reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/fd9329ab3368f59fe1fe808a189c51086bd4b6bd",
"reference": "fd9329ab3368f59fe1fe808a189c51086bd4b6bd",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
"reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
"shasum": ""
},
"require": {
@@ -2177,7 +2124,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.1"
"security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
},
"funding": [
{
@@ -2185,7 +2133,7 @@
"type": "github"
}
],
"time": "2023-02-10T16:53:14+00:00"
"time": "2023-08-31T06:24:48+00:00"
},
{
"name": "phpunit/php-invoker",
@@ -2252,16 +2200,16 @@
},
{
"name": "phpunit/php-text-template",
"version": "3.0.0",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d"
"reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d",
"reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
"reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
"shasum": ""
},
"require": {
@@ -2299,7 +2247,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-text-template/issues",
"source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0"
"security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
"source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
},
"funding": [
{
@@ -2307,7 +2256,7 @@
"type": "github"
}
],
"time": "2023-02-03T06:56:46+00:00"
"time": "2023-08-31T14:07:24+00:00"
},
{
"name": "phpunit/php-timer",
@@ -2370,16 +2319,16 @@
},
{
"name": "phpunit/phpunit",
"version": "10.0.19",
"version": "10.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "20c23e85c86e5c06d63538ba464e8054f4744e62"
"reference": "62bd7af13d282deeb95650077d28ba3600ca321c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/20c23e85c86e5c06d63538ba464e8054f4744e62",
"reference": "20c23e85c86e5c06d63538ba464e8054f4744e62",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/62bd7af13d282deeb95650077d28ba3600ca321c",
"reference": "62bd7af13d282deeb95650077d28ba3600ca321c",
"shasum": ""
},
"require": {
@@ -2393,7 +2342,7 @@
"phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
"php": ">=8.1",
"phpunit/php-code-coverage": "^10.0",
"phpunit/php-code-coverage": "^10.1.5",
"phpunit/php-file-iterator": "^4.0",
"phpunit/php-invoker": "^4.0",
"phpunit/php-text-template": "^3.0",
@@ -2403,8 +2352,8 @@
"sebastian/comparator": "^5.0",
"sebastian/diff": "^5.0",
"sebastian/environment": "^6.0",
"sebastian/exporter": "^5.0",
"sebastian/global-state": "^6.0",
"sebastian/exporter": "^5.1",
"sebastian/global-state": "^6.0.1",
"sebastian/object-enumerator": "^5.0",
"sebastian/recursion-context": "^5.0",
"sebastian/type": "^4.0",
@@ -2419,7 +2368,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "10.0-dev"
"dev-main": "10.4-dev"
}
},
"autoload": {
@@ -2451,7 +2400,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.0.19"
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.1"
},
"funding": [
{
@@ -2467,7 +2416,7 @@
"type": "tidelift"
}
],
"time": "2023-03-27T11:46:33+00:00"
"time": "2023-10-08T05:01:11+00:00"
},
{
"name": "psalm/plugin-phpunit",
@@ -2797,16 +2746,16 @@
},
{
"name": "sebastian/comparator",
"version": "5.0.0",
"version": "5.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "72f01e6586e0caf6af81297897bd112eb7e9627c"
"reference": "2db5010a484d53ebf536087a70b4a5423c102372"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c",
"reference": "72f01e6586e0caf6af81297897bd112eb7e9627c",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372",
"reference": "2db5010a484d53ebf536087a70b4a5423c102372",
"shasum": ""
},
"require": {
@@ -2817,7 +2766,7 @@
"sebastian/exporter": "^5.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0"
"phpunit/phpunit": "^10.3"
},
"type": "library",
"extra": {
@@ -2861,7 +2810,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0"
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
"source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1"
},
"funding": [
{
@@ -2869,20 +2819,20 @@
"type": "github"
}
],
"time": "2023-02-03T07:07:16+00:00"
"time": "2023-08-14T13:18:12+00:00"
},
{
"name": "sebastian/complexity",
"version": "3.0.0",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6"
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6",
"reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957",
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957",
"shasum": ""
},
"require": {
@@ -2895,7 +2845,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.0-dev"
"dev-main": "3.1-dev"
}
},
"autoload": {
@@ -2918,7 +2868,8 @@
"homepage": "https://github.com/sebastianbergmann/complexity",
"support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues",
"source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0"
"security": "https://github.com/sebastianbergmann/complexity/security/policy",
"source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0"
},
"funding": [
{
@@ -2926,20 +2877,20 @@
"type": "github"
}
],
"time": "2023-02-03T06:59:47+00:00"
"time": "2023-09-28T11:50:59+00:00"
},
{
"name": "sebastian/diff",
"version": "5.0.1",
"version": "5.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02"
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/aae9a0a43bff37bd5d8d0311426c87bf36153f02",
"reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
"reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b",
"shasum": ""
},
"require": {
@@ -2985,7 +2936,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"security": "https://github.com/sebastianbergmann/diff/security/policy",
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.1"
"source": "https://github.com/sebastianbergmann/diff/tree/5.0.3"
},
"funding": [
{
@@ -2993,7 +2944,7 @@
"type": "github"
}
],
"time": "2023-03-23T05:12:41+00:00"
"time": "2023-05-01T07:48:21+00:00"
},
{
"name": "sebastian/environment",
@@ -3061,16 +3012,16 @@
},
{
"name": "sebastian/exporter",
"version": "5.0.0",
"version": "5.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0"
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0",
"reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc",
"reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc",
"shasum": ""
},
"require": {
@@ -3084,7 +3035,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "5.0-dev"
"dev-main": "5.1-dev"
}
},
"autoload": {
@@ -3126,7 +3077,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0"
"security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1"
},
"funding": [
{
@@ -3134,20 +3086,20 @@
"type": "github"
}
],
"time": "2023-02-03T07:06:49+00:00"
"time": "2023-09-24T13:22:09+00:00"
},
{
"name": "sebastian/global-state",
"version": "6.0.0",
"version": "6.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "aab257c712de87b90194febd52e4d184551c2d44"
"reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44",
"reference": "aab257c712de87b90194febd52e4d184551c2d44",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4",
"reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4",
"shasum": ""
},
"require": {
@@ -3187,7 +3139,8 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0"
"security": "https://github.com/sebastianbergmann/global-state/security/policy",
"source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1"
},
"funding": [
{
@@ -3195,20 +3148,20 @@
"type": "github"
}
],
"time": "2023-02-03T07:07:38+00:00"
"time": "2023-07-19T07:19:23+00:00"
},
{
"name": "sebastian/lines-of-code",
"version": "2.0.0",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130"
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130",
"reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d",
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d",
"shasum": ""
},
"require": {
@@ -3244,7 +3197,8 @@
"homepage": "https://github.com/sebastianbergmann/lines-of-code",
"support": {
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0"
"security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1"
},
"funding": [
{
@@ -3252,7 +3206,7 @@
"type": "github"
}
],
"time": "2023-02-03T07:08:02+00:00"
"time": "2023-08-31T09:25:50+00:00"
},
{
"name": "sebastian/object-enumerator",
@@ -3881,16 +3835,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.2.1",
"version": "v3.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
"reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
"shasum": ""
},
"require": {
@@ -3899,7 +3853,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.3-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -3928,7 +3882,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0"
},
"funding": [
{
@@ -3944,7 +3898,7 @@
"type": "tidelift"
}
],
"time": "2023-03-01T10:25:55+00:00"
"time": "2023-05-23T14:45:45+00:00"
},
{
"name": "symfony/filesystem",
@@ -4142,16 +4096,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"shasum": ""
},
"require": {
@@ -4166,7 +4120,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -4204,7 +4158,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
},
"funding": [
{
@@ -4220,7 +4174,7 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
@@ -5025,11 +4979,11 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "~8.1.0 || ~8.2.0"
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
},
"platform-dev": [],
"platform-overrides": {
"php": "8.1.99"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

View File

@@ -94,11 +94,9 @@ class Config implements ConfigInterface
* @psalm-param ServiceManagerConfigurationType $a
* @psalm-param ServiceManagerConfigurationType $b
* @psalm-return ServiceManagerConfigurationType
* @psalm-suppress MixedReturnTypeCoercion
*/
private function merge(array $a, array $b)
{
/** @psalm-suppress MixedReturnTypeCoercion */
return ArrayUtils::merge($a, $b);
}
}

View File

@@ -89,7 +89,7 @@ use const E_USER_DEPRECATED;
* initializers?: InitializersConfiguration,
* invokables?: array<string,string>,
* lazy_services?: LazyServicesConfiguration,
* services?: array<string,object|array>,
* services?: array<string,object|array<mixed>>,
* shared?:array<string,bool>,
* shared_by_default?:bool,
* ...

View File

@@ -27,14 +27,14 @@
"extra": {
},
"require": {
"php": "~8.1.0 || ~8.2.0"
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
},
"require-dev": {
"laminas/laminas-coding-standard": "^2.5",
"phpbench/phpbench": "^1.2.9",
"phpunit/phpunit": "^10.0.16",
"phpbench/phpbench": "^1.2.15",
"phpunit/phpunit": "^10.5.8",
"psalm/plugin-phpunit": "^0.18.4",
"vimeo/psalm": "^5.8"
"vimeo/psalm": "^5.20.0"
},
"autoload": {
"psr-4": {

View File

@@ -7,6 +7,7 @@ namespace Laminas\Stdlib;
use Traversable;
use function array_shift;
use function get_object_vars;
use function is_array;
use function is_callable;
use function method_exists;
@@ -16,6 +17,10 @@ use function str_replace;
use function strtolower;
use function ucwords;
/**
* @template TValue
* @implements ParameterObjectInterface<string, TValue>
*/
abstract class AbstractOptions implements ParameterObjectInterface
{
// phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore,WebimpressCodingStandard.NamingConventions.ValidVariableName.NotCamelCapsProperty
@@ -33,7 +38,7 @@ abstract class AbstractOptions implements ParameterObjectInterface
/**
* Constructor
*
* @param array|Traversable|null $options
* @param iterable<string, TValue>|AbstractOptions<TValue>|null $options
*/
public function __construct($options = null)
{
@@ -45,7 +50,7 @@ abstract class AbstractOptions implements ParameterObjectInterface
/**
* Set one or more configuration properties
*
* @param array|Traversable|AbstractOptions $options
* @param iterable<string, TValue>|AbstractOptions<TValue> $options
* @throws Exception\InvalidArgumentException
* @return AbstractOptions Provides fluent interface
*/
@@ -77,19 +82,20 @@ abstract class AbstractOptions implements ParameterObjectInterface
/**
* Cast to array
*
* @return array
* @return array<string, TValue>
*/
public function toArray()
{
$array = [];
/** @param string[] $letters */
$transform = static function (array $letters): string {
/** @var list<string> $letters */
$letter = array_shift($letters);
return '_' . strtolower($letter);
};
foreach ($this as $key => $value) {
/** @psalm-var TValue $value */
foreach (get_object_vars($this) as $key => $value) {
if ($key === '__strictMode__') {
continue;
}
@@ -106,7 +112,7 @@ abstract class AbstractOptions implements ParameterObjectInterface
* @see ParameterObject::__set()
*
* @param string $key
* @param mixed $value
* @param TValue|null $value
* @throws Exception\BadMethodCallException
* @return void
*/
@@ -137,7 +143,7 @@ abstract class AbstractOptions implements ParameterObjectInterface
*
* @param string $key
* @throws Exception\BadMethodCallException
* @return mixed
* @return TValue
*/
public function __get($key)
{

View File

@@ -333,27 +333,27 @@ class ArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Count
/**
* Sets the value at the specified key to value
*
* @param TKey $key
* @param TKey $offset
* @param TValue $value
* @return void
*/
#[ReturnTypeWillChange]
public function offsetSet(mixed $key, mixed $value)
public function offsetSet(mixed $offset, mixed $value)
{
$this->storage[$key] = $value;
$this->storage[$offset] = $value;
}
/**
* Unsets the value at the specified key
*
* @param TKey $key
* @param TKey $offset
* @return void
*/
#[ReturnTypeWillChange]
public function offsetUnset(mixed $key)
public function offsetUnset(mixed $offset)
{
if ($this->offsetExists($key)) {
unset($this->storage[$key]);
if ($this->offsetExists($offset)) {
unset($this->storage[$offset]);
}
}

View File

@@ -35,11 +35,15 @@ abstract class ArrayUtils
{
/**
* Compatibility Flag for ArrayUtils::filter
*
* @deprecated
*/
public const ARRAY_FILTER_USE_BOTH = 1;
/**
* Compatibility Flag for ArrayUtils::filter
*
* @deprecated
*/
public const ARRAY_FILTER_USE_KEY = 2;
@@ -188,13 +192,15 @@ abstract class ArrayUtils
* non-strict check is implemented. if $strict = -1, the default in_array
* non-strict behaviour is used.
*
* @deprecated This method will be removed in version 4.0 of this component
*
* @param array $haystack
* @param int|bool $strict
* @return bool
*/
public static function inArray(mixed $needle, array $haystack, $strict = false)
{
if (! $strict) {
if ((bool) $strict === false) {
if (is_int($needle) || is_float($needle)) {
$needle = (string) $needle;
}

View File

@@ -4,28 +4,33 @@ declare(strict_types=1);
namespace Laminas\Stdlib;
/**
* @template TKey of string
* @template TValue
*/
interface ParameterObjectInterface
{
/**
* @param string $key
* @param TKey $key
* @param TValue|null $value
* @return void
*/
public function __set($key, mixed $value);
/**
* @param string $key
* @return mixed
* @param TKey $key
* @return TValue
*/
public function __get($key);
/**
* @param string $key
* @param TKey $key
* @return bool
*/
public function __isset($key);
/**
* @param string $key
* @param TKey $key
* @return void
*/
public function __unset($key);

View File

@@ -357,7 +357,7 @@ class PriorityQueue implements Countable, IteratorAggregate, Serializable
if (! $this->queue instanceof \SplPriorityQueue) {
throw new Exception\DomainException(sprintf(
'PriorityQueue expects an internal queue of type SplPriorityQueue; received "%s"',
$this->queue::class
$queue::class
));
}
}

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Laminas\Stdlib;
use ReturnTypeWillChange;
use Serializable;
use UnexpectedValueException;
@@ -37,17 +38,18 @@ class SplPriorityQueue extends \SplPriorityQueue implements Serializable
* Utilizes {@var $serial} to ensure that values of equal priority are
* emitted in the same order in which they are inserted.
*
* @param TValue $datum
* @param TValue $value
* @param TPriority $priority
* @return void
*/
public function insert($datum, $priority)
#[ReturnTypeWillChange] // Inherited return type should be bool
public function insert($value, $priority)
{
if (! is_array($priority)) {
$priority = [$priority, $this->serial--];
}
parent::insert($datum, $priority);
parent::insert($value, $priority);
}
/**

View File

@@ -3,19 +3,19 @@
[![Build Status](https://github.com/laminas/laminas-validator/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/laminas/laminas-validator/actions/workflows/continuous-integration.yml)
> ## 🇷🇺 Русским гражданам
>
>
> Мы, участники Laminas, родились и живем в разных странах. У многих из нас есть друзья, родственники и коллеги как в России, так и в Украине. Некоторые из нас родились в России. Некоторые из нас живут в России. У некоторых бабушки и дедушки сражались с фашистами во Второй мировой войне. Здесь никто не поддерживает фашизм.
>
>
> У одного из нас есть украинская родственница, которая спаслась из дома вместе с сыном. Поезд задержался из-за бомбежки на дороге впереди. У нас есть друзья, которые прячутся в бомбоубежищах. Мы с тревогой ждем весточки от них после воздушных налетов, которые беспорядочно наносят удары и попадают по больницам, школам, детским садам и домам. Мы не берем это из каких-либо СМИ. Мы наблюдаем это напрямую.
>
>
> Вы доверяете нам достаточно, чтоб использовать наши программы, и мы просим вас довериться нам вновь. Мы нуждаемся в помощи. Выходите и протестуйте против этой бесполезной войны. Остановите кровопролитие. Скажите "Нет войне!"
>
>
> ## 🇺🇸 To Citizens of Russia
>
>
> We at Laminas come from all over the world. Many of us have friends, family and colleagues in both Russia and Ukraine. Some of us were born in Russia. Some of us currently live in Russia. Some have grandparents who fought Nazis in World War II. Nobody here supports fascism.
>
>
> One team member has a Ukrainian relative who fled her home with her son. The train was delayed due to bombing on the road ahead. We have friends who are hiding in bomb shelters. We anxiously follow up on them after the air raids, which indiscriminately fire at hospitals, schools, kindergartens and houses. We're not taking this from any media. These are our actual experiences.
>
>
> You trust us enough to use our software. We ask that you trust us to say the truth on this. We need your help. Go out and protest this unnecessary war. Stop the bloodshed. Say "stop the war!"
laminas-validator provides a set of commonly needed validators. It also provides a

View File

@@ -1,19 +1,13 @@
<?php
/**
* @see https://github.com/laminas/laminas-validator for the canonical source repository
* @copyright https://github.com/laminas/laminas-validator/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-validator/blob/master/LICENSE.md New BSD License
*/
declare(strict_types=1);
use Laminas\Http\Client;
use Laminas\Validator\Hostname;
require __DIR__ . '/../vendor/autoload.php';
define('IANA_URL', 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt');
define('LAMINAS_HOSTNAME_VALIDATOR_FILE', __DIR__.'/../src/Hostname.php');
const IANA_URL = 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt';
const LAMINAS_HOSTNAME_VALIDATOR_FILE = __DIR__ . '/../src/Hostname.php';
if (! file_exists(LAMINAS_HOSTNAME_VALIDATOR_FILE) || ! is_readable(LAMINAS_HOSTNAME_VALIDATOR_FILE)) {
printf("Error: cannot read file '%s'%s", LAMINAS_HOSTNAME_VALIDATOR_FILE, PHP_EOL);
@@ -26,34 +20,15 @@ if (! is_writable(LAMINAS_HOSTNAME_VALIDATOR_FILE)) {
}
/** @psalm-var list<string> $newFileContent */
$newFileContent = []; // new file content
$insertDone = false; // becomes 'true' when we find start of $validTlds declaration
$insertFinish = false; // becomes 'true' when we find end of $validTlds declaration
$checkOnly = isset($argv[1]) ? $argv[1] === '--check-only' : false;
$response = getOfficialTLDs();
$ianaVersion = getVersionFromString('Version', strtok($response->getBody(), "\n"));
$validatorVersion = getVersionFromString('IanaVersion', file_get_contents(LAMINAS_HOSTNAME_VALIDATOR_FILE));
$newFileContent = []; // new file content
$insertDone = false; // becomes 'true' when we find start of $validTlds declaration
$insertFinish = false; // becomes 'true' when we find end of $validTlds declaration
$checkOnly = isset($argv[1]) ? $argv[1] === '--check-only' : false;
$response = getOfficialTLDs();
if ($checkOnly && $ianaVersion > $validatorVersion) {
printf(
'TLDs must be updated, please run `php bin/update_hostname_validator.php` and push your changes%s',
PHP_EOL
);
exit(1);
}
if ($checkOnly) {
printf('TLDs are up-to-date%s', PHP_EOL);
exit(0);
}
foreach (file(LAMINAS_HOSTNAME_VALIDATOR_FILE) as $line) {
// Replace old version number with new one
if (preg_match('/\*\s+IanaVersion\s+\d+/', $line, $matches)) {
$newFileContent[] = sprintf(" * IanaVersion %s\n", $ianaVersion);
continue;
}
$currentFileContent = file(LAMINAS_HOSTNAME_VALIDATOR_FILE);
foreach ($currentFileContent as $line) {
if ($insertDone === $insertFinish) {
// Outside of $validTlds definition; keep line as-is
$newFileContent[] = $line;
@@ -75,7 +50,7 @@ foreach (file(LAMINAS_HOSTNAME_VALIDATOR_FILE) as $line) {
// Detect where the $validTlds declaration begins
if (preg_match('/^\s+protected\s+\$validTlds\s+=\s+\[\s*$/', $line)) {
$newFileContent = array_merge($newFileContent, getNewValidTlds($response->getBody()));
$newFileContent = array_merge($newFileContent, getNewValidTlds($response));
$insertDone = true;
}
}
@@ -85,11 +60,24 @@ if (! $insertDone) {
exit(1);
}
if (!$insertFinish) {
if (! $insertFinish) {
printf('Error: cannot find end of $validTlds declaration%s', PHP_EOL);
exit(1);
}
if ($currentFileContent === $newFileContent) {
printf('Nothing to do. Validator has no TLD changes.%s', PHP_EOL);
exit(0);
}
if ($checkOnly) {
printf(
'TLDs must be updated, please run `php bin/update_hostname_validator.php` and push your changes%s',
PHP_EOL
);
exit(1);
}
if (false === @file_put_contents(LAMINAS_HOSTNAME_VALIDATOR_FILE, $newFileContent)) {
printf('Error: cannot write info file "%s"%s', LAMINAS_HOSTNAME_VALIDATOR_FILE, PHP_EOL);
exit(1);
@@ -100,59 +88,34 @@ exit(0);
/**
* Get Official TLDs
*
* @return \Laminas\Http\Response
* @throws Exception
*/
function getOfficialTLDs()
function getOfficialTLDs(): string
{
$client = new Client();
$client->setOptions([
'adapter' => 'Laminas\Http\Client\Adapter\Curl',
]);
$client->setUri(IANA_URL);
$client->setMethod('GET');
try {
return file_get_contents(IANA_URL);
} catch (Throwable $e) {
printf(
'Downloading the IANA TLD list failed: %s',
$e->getMessage(),
);
$response = $client->send();
if (! $response->isSuccess()) {
throw new \Exception(sprintf("Error: cannot get '%s'%s", IANA_URL, PHP_EOL));
exit(1);
}
return $response;
}
/**
* Extract the first match of a string like
* "Version 2015072300" from the given string
*
* @param string $prefix
* @param string $string
* @return string
* @throws Exception
*/
function getVersionFromString($prefix, $string)
{
$matches = [];
if (! preg_match(sprintf('/%s\s+((\d+)?)/', $prefix), $string, $matches)) {
throw new Exception('Error: cannot get last update date');
}
return $matches[1];
}
/**
* Extract new Valid TLDs from a string containing one per line.
*
* @param string $string
* @return list<string>
*/
function getNewValidTlds($string)
function getNewValidTlds(string $string): array
{
$decodePunycode = getPunycodeDecoder();
// Get new TLDs from the list previously fetched
$newValidTlds = [];
foreach (preg_grep('/^[^#]/', preg_split("#\r?\n#", $string)) as $line) {
$newValidTlds []= sprintf(
$newValidTlds [] = sprintf(
"%s'%s',\n",
str_repeat(' ', 8),
$decodePunycode(strtolower($line))
@@ -183,7 +146,7 @@ function getPunycodeDecoder()
}
$hostnameValidator = new Hostname();
$reflection = new ReflectionClass(get_class($hostnameValidator));
$reflection = new ReflectionClass($hostnameValidator::class);
$decodePunyCode = $reflection->getMethod('decodePunycode');
return function ($encode) use ($hostnameValidator, $decodePunyCode) {

View File

@@ -31,24 +31,23 @@
}
},
"require": {
"php": "~8.1.0 || ~8.2.0",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"laminas/laminas-servicemanager": "^3.21.0",
"laminas/laminas-stdlib": "^3.13",
"psr/http-message": "^1.0.1 || ^2.0.0"
},
"require-dev": {
"laminas/laminas-coding-standard": "^2.5",
"laminas/laminas-db": "^2.18",
"laminas/laminas-filter": "^2.32",
"laminas/laminas-http": "^2.18",
"laminas/laminas-i18n": "^2.23",
"laminas/laminas-session": "^2.16",
"laminas/laminas-uri": "^2.10.0",
"phpunit/phpunit": "^10.1.3",
"psalm/plugin-phpunit": "^0.18.4",
"psr/http-client": "^1.0.2",
"psr/http-factory": "^1.0.2",
"vimeo/psalm": "^5.11"
"laminas/laminas-db": "^2.20",
"laminas/laminas-filter": "^2.35.2",
"laminas/laminas-i18n": "^2.26.0",
"laminas/laminas-session": "^2.20",
"laminas/laminas-uri": "^2.11.0",
"phpunit/phpunit": "^10.5.20",
"psalm/plugin-phpunit": "^0.19.0",
"psr/http-client": "^1.0.3",
"psr/http-factory": "^1.1.0",
"vimeo/psalm": "^5.24.0"
},
"suggest": {
"laminas/laminas-db": "Laminas\\Db component, required by the (No)RecordExists validator",
@@ -66,9 +65,6 @@
}
},
"autoload-dev": {
"files": [
"test/autoload.php"
],
"psr-4": {
"LaminasTest\\Validator\\": "test/"
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,14 +2,16 @@
namespace Laminas\Validator;
use Laminas\Stdlib\ArrayUtils;
use Laminas\Validator\Barcode\AdapterInterface;
use Laminas\Validator\Exception\InvalidArgumentException;
use Traversable;
use function array_key_exists;
use function assert;
use function class_exists;
use function get_debug_type;
use function is_array;
use function is_string;
use function property_exists;
use function sprintf;
use function strtolower;
use function substr;
@@ -38,8 +40,14 @@ class Barcode extends AbstractValidator
protected $messageVariables = [
'length' => ['options' => 'length'],
];
/** @var array<string, mixed> */
/**
* @var array{
* adapter: null|AdapterInterface,
* options: null|array<string, mixed>,
* length: null|int|array,
* useChecksum: null|bool,
* }
*/
protected $options = [
'adapter' => null, // Barcode adapter Laminas\Validator\Barcode\AbstractAdapter
'options' => null, // Options for this adapter
@@ -50,33 +58,37 @@ class Barcode extends AbstractValidator
/**
* Constructor for barcodes
*
* @param array|string $options Options to use
* @param iterable<string, mixed>|null|string|AdapterInterface $options Options to use
*/
public function __construct($options = null)
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if ($options === null) {
$options = [];
}
if (is_array($options)) {
if (array_key_exists('options', $options)) {
$options['options'] = ['options' => $options['options']];
}
} elseif ($options instanceof Traversable) {
if (property_exists($options, 'options')) {
$options['options'] = ['options' => $options['options']];
}
} else {
if (is_string($options) || $options instanceof AdapterInterface) {
$options = ['adapter' => $options];
}
if (! is_array($options)) {
throw new InvalidArgumentException(sprintf(
'Options should be an array, a string representing the name of an adapter, or an adapter instance. '
. 'Received "%s"',
get_debug_type($options),
));
}
parent::__construct($options);
}
/**
* Returns the set adapter
*
* @return Barcode\AbstractAdapter
* @return AdapterInterface
*/
public function getAdapter()
{
@@ -84,16 +96,18 @@ class Barcode extends AbstractValidator
$this->setAdapter('Ean13');
}
assert($this->options['adapter'] instanceof Barcode\AdapterInterface);
return $this->options['adapter'];
}
/**
* Sets a new barcode adapter
*
* @param string|Barcode\AbstractAdapter $adapter Barcode adapter to use
* @param string|AdapterInterface $adapter Barcode adapter to use
* @param array $options Options for this adapter
* @return $this
* @throws Exception\InvalidArgumentException
* @throws InvalidArgumentException
*/
public function setAdapter($adapter, $options = null)
{
@@ -102,14 +116,14 @@ class Barcode extends AbstractValidator
$adapter = 'Laminas\\Validator\\Barcode\\' . $adapter;
if (! class_exists($adapter)) {
throw new Exception\InvalidArgumentException('Barcode adapter matching "' . $adapter . '" not found');
throw new InvalidArgumentException('Barcode adapter matching "' . $adapter . '" not found');
}
$adapter = new $adapter($options);
}
if (! $adapter instanceof Barcode\AdapterInterface) {
throw new Exception\InvalidArgumentException(
throw new InvalidArgumentException(
sprintf(
'Adapter %s does not implement Laminas\\Validator\\Barcode\\AdapterInterface',
get_debug_type($adapter)
@@ -125,7 +139,7 @@ class Barcode extends AbstractValidator
/**
* Returns the checksum option
*
* @return string
* @return string|null
*/
public function getChecksum()
{
@@ -136,7 +150,7 @@ class Barcode extends AbstractValidator
* Sets if checksum should be validated, if no value is given the actual setting is returned
*
* @param null|bool $checksum
* @return Barcode\AbstractAdapter|bool
* @return AdapterInterface|bool
*/
public function useChecksum($checksum = null)
{

View File

@@ -16,7 +16,12 @@ abstract class AbstractAdapter implements AdapterInterface
/**
* Allowed options for this adapter
*
* @var array
* @var array{
* length: int|array|'even'|'odd'|null,
* characters: int|string|array|null,
* checksum: null|string,
* useChecksum: null|bool,
* }
*/
protected $options = [
'length' => null, // Allowed barcode lengths, integer, array, string
@@ -115,7 +120,7 @@ abstract class AbstractAdapter implements AdapterInterface
public function hasValidChecksum($value)
{
$checksum = $this->getChecksum();
if (! empty($checksum)) {
if ($checksum !== null) {
if (method_exists($this, $checksum)) {
return $this->$checksum($value);
}
@@ -127,7 +132,7 @@ abstract class AbstractAdapter implements AdapterInterface
/**
* Returns the allowed barcode length
*
* @return int|array
* @return int|array|string|null
*/
public function getLength()
{
@@ -137,7 +142,7 @@ abstract class AbstractAdapter implements AdapterInterface
/**
* Returns the allowed characters
*
* @return int|string|array
* @return int|string|array|null
*/
public function getCharacters()
{
@@ -147,7 +152,7 @@ abstract class AbstractAdapter implements AdapterInterface
/**
* Returns the checksum function name
*
* @return string
* @return string|null
*/
public function getChecksum()
{
@@ -157,7 +162,7 @@ abstract class AbstractAdapter implements AdapterInterface
/**
* Sets the checksum validation method
*
* @param callable $checksum Checksum method to call
* @param string $checksum Checksum method to call
* @return $this
*/
protected function setChecksum($checksum)
@@ -169,8 +174,7 @@ abstract class AbstractAdapter implements AdapterInterface
/**
* Sets the checksum validation, if no value is given, the actual setting is returned
*
* @param bool $check
* @return AbstractAdapter|bool
* @inheritDoc
*/
public function useChecksum($check = null)
{
@@ -197,7 +201,7 @@ abstract class AbstractAdapter implements AdapterInterface
/**
* Sets the allowed characters of this barcode
*
* @param int $characters
* @param int|string|array $characters
* @return $this
*/
protected function setCharacters($characters)

View File

@@ -31,29 +31,30 @@ interface AdapterInterface
/**
* Returns the allowed barcode length
*
* @return int|array
* @return int|string|array|null
*/
public function getLength();
/**
* Returns the allowed characters
*
* @return int|string|array
* @return int|string|array|null
*/
public function getCharacters();
/**
* Returns if barcode uses a checksum
*
* @return bool|int|string
* @return string|null
*/
public function getChecksum();
/**
* Sets the checksum validation, if no value is given, the actual setting is returned
*
* @param bool $check
* @return AbstractAdapter|bool
* @param bool|null $check
* @return $this|bool
* @psalm-return ($check is null ? bool : static)
*/
public function useChecksum($check = null);
}

View File

@@ -27,29 +27,29 @@ class Codabar extends AbstractAdapter
*/
public function hasValidCharacters($value)
{
if (strpbrk($value, 'ABCD')) {
if (strpbrk($value, 'ABCD') !== false) {
$first = $value[0];
if (! strpbrk($first, 'ABCD')) {
if (strpbrk($first, 'ABCD') === false) {
// Missing start char
return false;
}
$last = substr($value, -1, 1);
if (! strpbrk($last, 'ABCD')) {
if (strpbrk($last, 'ABCD') === false) {
// Missing stop char
return false;
}
$value = substr($value, 1, -1);
} elseif (strpbrk($value, 'TN*E')) {
} elseif (strpbrk($value, 'TN*E') !== false) {
$first = $value[0];
if (! strpbrk($first, 'TN*E')) {
if (strpbrk($first, 'TN*E') === false) {
// Missing start char
return false;
}
$last = substr($value, -1, 1);
if (! strpbrk($last, 'TN*E')) {
if (strpbrk($last, 'TN*E') === false) {
// Missing stop char
return false;
}

View File

@@ -505,7 +505,7 @@ class Code128 extends AbstractAdapter
}
$value = $strWrapper->substr($value, 1, null);
while ($strWrapper->strpos($value, 'Š') || ($value !== '')) {
while ($strWrapper->strpos((string) $value, 'Š') !== false || ((string) $value !== '')) {
$char = $strWrapper->substr($value, 0, 1);
if ($read === 'C') {
$char = $strWrapper->substr($value, 0, 2);

View File

@@ -9,6 +9,19 @@ use function array_merge;
use function call_user_func_array;
use function is_callable;
/**
* @psalm-type OptionsProperty = array{
* callback: callable|null,
* callbackOptions: array<array-key, mixed>,
* throwExceptions: bool,
* }
* @psalm-type OptionsArgument = array{
* callback: callable,
* callbackOptions?: array<array-key, mixed>,
* throwExceptions?: bool,
* ...<string, mixed>
* }
*/
class Callback extends AbstractValidator
{
/**
@@ -34,18 +47,15 @@ class Callback extends AbstractValidator
/**
* Default options to set for the validator
*
* @var mixed
* @var OptionsProperty
*/
protected $options = [
'callback' => null, // Callback in a call_user_func format, string || array
'callbackOptions' => [], // Options for the callback
'throwExceptions' => false, // Whether to throw exceptions raised within the callback or not
];
/**
* Constructor
*
* @param array|callable $options
*/
/** @param OptionsArgument|callable $options */
public function __construct($options = null)
{
if (is_callable($options)) {
@@ -58,7 +68,7 @@ class Callback extends AbstractValidator
/**
* Returns the set callback
*
* @return mixed
* @return callable|null
*/
public function getCallback()
{
@@ -68,7 +78,7 @@ class Callback extends AbstractValidator
/**
* Sets the callback
*
* @param string|array|callable $callback
* @param callable $callback
* @return $this Provides a fluent interface
* @throws InvalidArgumentException
*/
@@ -85,7 +95,7 @@ class Callback extends AbstractValidator
/**
* Returns the set options for the callback
*
* @return mixed
* @return array<array-key, mixed>
*/
public function getCallbackOptions()
{
@@ -95,6 +105,7 @@ class Callback extends AbstractValidator
/**
* Sets options for the callback
*
* @param array<array-key, mixed> $options
* @return $this Provides a fluent interface
*/
public function setCallbackOptions(mixed $options)
@@ -118,7 +129,7 @@ class Callback extends AbstractValidator
$options = $this->getCallbackOptions();
$callback = $this->getCallback();
if (empty($callback)) {
if (! is_callable($callback)) {
throw new InvalidArgumentException('No callback given');
}
@@ -139,8 +150,16 @@ class Callback extends AbstractValidator
$this->error(self::INVALID_VALUE);
return false;
}
} catch (Exception) {
} catch (Exception $exception) {
/**
* Intentionally excluding catchable \Error as they are indicative of a bug and should not be suppressed
*/
$this->error(self::INVALID_CALLBACK);
if ($this->options['throwExceptions'] === true) {
throw $exception;
}
return false;
}

View File

@@ -25,12 +25,14 @@ class ConfigProvider
{
return [
'aliases' => [
'ValidatorManager' => ValidatorPluginManager::class,
Translator\TranslatorInterface::class => Translator\Translator::class,
'ValidatorManager' => ValidatorPluginManager::class,
// Legacy Zend Framework aliases
'Zend\Validator\ValidatorPluginManager' => ValidatorPluginManager::class,
],
'factories' => [
Translator\Translator::class => Translator\TranslatorFactory::class,
ValidatorPluginManager::class => ValidatorPluginManagerFactory::class,
],
];

View File

@@ -362,7 +362,7 @@ class CreditCard extends AbstractValidator
/**
* Returns true if and only if $value follows the Luhn algorithm (mod-10 checksum)
*
* @param string $value
* @param mixed $value
* @return bool
*/
public function isValid(

View File

@@ -114,8 +114,8 @@ class Csrf extends AbstractValidator
/**
* Does the provided token match the one generated?
*
* @param string $value
* @param mixed $context
* @param mixed $value
* @param mixed $context
* @return bool
*/
public function isValid($value, $context = null)
@@ -132,7 +132,7 @@ class Csrf extends AbstractValidator
$tokenFromValue = $this->getTokenFromHash($value);
$tokenFromHash = $this->getTokenFromHash($hash);
if (! $tokenFromValue || ! $tokenFromHash || ($tokenFromValue !== $tokenFromHash)) {
if ($tokenFromValue === null || $tokenFromHash === null || ($tokenFromValue !== $tokenFromHash)) {
$this->error(self::NOT_SAME);
return false;
}
@@ -334,11 +334,11 @@ class Csrf extends AbstractValidator
*
* @todo remove, here for BC
*/
if (! $tokenId && isset($session->hash)) {
if ($tokenId === null && isset($session->hash)) {
return $session->hash;
}
if ($tokenId && isset($session->tokenList[$tokenId])) {
if ($tokenId !== null && isset($session->tokenList[$tokenId])) {
return $this->formatHash($session->tokenList[$tokenId], $tokenId);
}

View File

@@ -96,7 +96,7 @@ class Date extends AbstractValidator
*/
public function setFormat($format = self::FORMAT_DEFAULT)
{
$this->format = empty($format) ? self::FORMAT_DEFAULT : $format;
$this->format = $format === null || $format === '' ? self::FORMAT_DEFAULT : $format;
return $this;
}

View File

@@ -472,7 +472,7 @@ class DateStep extends Date
$minSteps = floor($minSteps / $requiredStepIterations);
}
return [(int) $minSteps, $minSteps ? (int) $requiredStepIterations : 0];
return [(int) $minSteps, $minSteps !== 0 ? (int) $requiredStepIterations : 0];
}
/**

View File

@@ -23,6 +23,8 @@ use function is_array;
/**
* Class for Database record validation
*
* @deprecated This class will be removed in version 3.0 of this component. There is no replacement.
*/
abstract class AbstractDb extends AbstractValidator implements AdapterAwareInterface
{

View File

@@ -6,6 +6,8 @@ use Laminas\Validator\Exception;
/**
* Confirms a record does not exist in a table.
*
* @deprecated This class will be removed in version 3.0 of this component. There is no replacement.
*/
class NoRecordExists extends AbstractDb
{

View File

@@ -6,6 +6,8 @@ use Laminas\Validator\Exception;
/**
* Confirms a record exists in a table.
*
* @deprecated This class will be removed in version 3.0 of this component. There is no replacement.
*/
class RecordExists extends AbstractDb
{

View File

@@ -17,7 +17,7 @@ class Digits extends AbstractValidator
/**
* Digits filter used for validation
*
* @var DigitsFilter
* @var DigitsFilter|null
*/
protected static $filter;
@@ -35,7 +35,7 @@ class Digits extends AbstractValidator
/**
* Returns true if and only if $value only contains digit characters
*
* @param string $value
* @param mixed $value
* @return bool
*/
public function isValid($value)

View File

@@ -313,7 +313,7 @@ class EmailAddress extends AbstractValidator
$host = [$host];
}
if (empty($host)) {
if (! is_array($host) || $host === []) {
return false;
}
@@ -437,9 +437,9 @@ class EmailAddress extends AbstractValidator
}
}
if (! $result) {
if ($result === false) {
$this->error(self::INVALID_MX_RECORD);
return $result;
return false;
}
if (! $this->options['useDeepMxCheck']) {
@@ -454,7 +454,7 @@ class EmailAddress extends AbstractValidator
$reserved = false;
}
if (! is_string($hostname) || ! trim($hostname)) {
if (trim($hostname) === '') {
continue;
}
@@ -568,7 +568,7 @@ class EmailAddress extends AbstractValidator
$local = $this->validateLocalPart();
// If both parts valid, return true
return ($local && $length) && (! $this->options['useDomainCheck'] || $hostname);
return ($local && $length) && (! $this->options['useDomainCheck'] || $hostname !== false);
}
/**
@@ -581,9 +581,13 @@ class EmailAddress extends AbstractValidator
{
if (extension_loaded('intl')) {
if (defined('INTL_IDNA_VARIANT_UTS46')) {
return idn_to_ascii($email, 0, INTL_IDNA_VARIANT_UTS46) ?: $email;
$value = idn_to_ascii($email, 0, INTL_IDNA_VARIANT_UTS46);
return $value !== false ? $value : $email;
}
return idn_to_ascii($email) ?: $email;
$value = idn_to_ascii($email);
return $value !== false ? $value : $email;
}
return $email;
}
@@ -608,9 +612,13 @@ class EmailAddress extends AbstractValidator
// But not when the source string is long enough.
// Thus we default to source string ourselves.
if (defined('INTL_IDNA_VARIANT_UTS46')) {
return idn_to_utf8($email, 0, INTL_IDNA_VARIANT_UTS46) ?: $email;
$value = idn_to_utf8($email, 0, INTL_IDNA_VARIANT_UTS46);
return $value !== false ? $value : $email;
}
return idn_to_utf8($email) ?: $email;
$value = idn_to_utf8($email);
return $value !== false ? $value : $email;
}
return $email;
}

View File

@@ -199,6 +199,6 @@ class Explode extends AbstractValidator implements ValidatorPluginManagerAwareIn
}
}
return ! $this->abstractOptions['messages'];
return $this->abstractOptions['messages'] === [];
}
}

View File

@@ -245,7 +245,7 @@ class Count extends AbstractValidator
/**
* Throws an error of the given type
*
* @param string $file
* @param string|null|array $file
* @param string $errorType
* @return false
*/

View File

@@ -2,6 +2,8 @@
namespace Laminas\Validator\File;
use Psr\Http\Message\UploadedFileInterface;
use function array_merge;
use function class_exists;
use function explode;
@@ -9,6 +11,7 @@ use function finfo_file;
use function finfo_open;
use function in_array;
use function is_readable;
use function is_string;
use const FILEINFO_MIME_TYPE;
@@ -23,7 +26,7 @@ class ExcludeMimeType extends MimeType
public const NOT_DETECTED = 'fileExcludeMimeTypeNotDetected';
public const NOT_READABLE = 'fileExcludeMimeTypeNotReadable';
/** @var array Error message templates */
/** @inheritDoc */
protected $messageTemplates = [
self::FALSE_TYPE => "File has an incorrect mimetype of '%type%'",
self::NOT_DETECTED => 'The mimetype could not be detected from the file',
@@ -35,8 +38,8 @@ class ExcludeMimeType extends MimeType
* of mimetypes can be checked. If you give for example "image" all image
* mime types will not be accepted like "image/gif", "image/jpeg" and so on.
*
* @param string|array $value Real file to check for mimetype
* @param array $file File data from \Laminas\File\Transfer\Transfer (optional)
* @param string|array|UploadedFileInterface $value Real file to check for mimetype
* @param array $file File data from \Laminas\File\Transfer\Transfer (optional)
* @return bool
*/
public function isValid($value, $file = null)
@@ -53,7 +56,7 @@ class ExcludeMimeType extends MimeType
$mimefile = $this->getMagicFile();
if (class_exists('finfo', false)) {
if (! $this->isMagicFileDisabled() && (! empty($mimefile) && empty($this->finfo))) {
if (! $this->isMagicFileDisabled() && (is_string($mimefile) && empty($this->finfo))) {
$this->finfo = finfo_open(FILEINFO_MIME_TYPE, $mimefile);
}
@@ -67,11 +70,11 @@ class ExcludeMimeType extends MimeType
}
}
if (empty($this->type) && $this->getHeaderCheck()) {
if (! is_string($this->type) && $this->getHeaderCheck()) {
$this->type = $fileInfo['filetype'];
}
if (empty($this->type)) {
if (! is_string($this->type)) {
$this->error(self::NOT_DETECTED);
return false;
}

Some files were not shown because too many files have changed in this diff Show More