PHPFUI Tests Latest Packagist release

PHP Wrapper for the Foundation CSS Framework

PHPFUI, PHP Foundation User Interface, is a modern PHP library that produces HTML formated for Foundation. It does everything you need for a fully functional Foundation page, with the power of an OO language. It currently uses Foundation 6.6.

"I was surprised that people were prepared to write HTML. In my initial requirements for this thing, I had assumed, as an absolute pre-condition, that nobody would have to do HTML or deal with URLs. If you use the original World Wide Web program, you never see a URL or have to deal with HTML. You're presented with the raw information. You then input more information. So you are linking information to information--like using a word processor. That was a surprise to me--that people were prepared to painstakingly write HTML."

Sir Tim Berners-Lee, inventor of the World Wide Web

Using PHPFUI for view output will produce 100% valid HTML and insulate you from future changes to Foundation, your custom HMTL layouts, CSS and JS library changes. You write to an abstract concept (I want a checkbox here), and the library will output a checkbox formatted for Foundation. You can inherit from CheckBox and add your own take on a checkbox, and when the graphic designer decides they have the most awesome checkbox ever, you simply change your CheckBox class, and it is changed on every page system wide.

Don't write HTML by hand!

Usage

namespace PHPFUI;
$page = new Page();
$form = new Form($page);
$fieldset = new FieldSet('A basic input form');
$time = new Input\Time($page, 'time', 'Enter A Time in 15 minute increments');
$time->setRequired();
$date = new Input\Date($page, 'date', 'Pick A Date');
$fieldset->add(new MultiColumn($time, $date));
$fieldset->add(new Input\TextArea('text', 'Enter some text'));
$fieldset->add(new Submit());
$form->add($fieldset);
$page->add($form);
$page->addStyleSheet('/css/styles.css');
echo $page;

Installation Instructions

composer require phpfui/phpfui

Then run update.php from the vendor/phpfui/phpfui directory and supply the path to your public directory / the directory for the various JS and CSS files PHPFUI uses. This will copy all required public files into your public directory. For example:

php vendor/phpfui/phpfui/update.php public/PHPFUI

The PHPFUI library defaults to your-public-directory/PHPFUI, it can be overridden, but it is suggested to use PHPFUI to keep everything in one place. update.php should be run when ever you update PHPFUI.

Versioning

Versioning will match the Foundation versions for Major semantic versions. PHPUI will always support the most recent version of Foundation possible for the Major version. PHPFUI Minor version will include breaking changes and may incorporate changes for the latest version of Foundation. The PHPFUI Patch version will include non breaking changes or additions. So PHPFUI Version 6.0.0 would be the first version of the library, 6.0.1 would be the first patch of PHPFUI. Both should work with any Foundation 6.x version. PHPFUI 6.1.0 will track PHP 7.4 - 8.1, 6.2.0 will track 8.0 - 8.2, but both will still track Foundation 6.x. PHPFUI 7.0.0 would track Foundation 7.x series on currently supported versions of PHP.

Depreciation and Foundation changes

Since major versions of Foundation have in the past depreciated and obsoleted things, PHPFUI will track the latest version of Foundation for class names and functionality. However, when Foundation makes a breaking change or removes something, PHPFUI will continue to support the old functionality as best as possible in the new Foundation framework. Depreciated classes will be put in the \PHPFUI\Vx namespace (where x would be the prior Major Foundation version containing that feature). So if something gets depreciated in a newer version of Foundation, you simply will need to change your code from \PHPFUI\Example to \PHPFUI\V6\Example. The depreciated namespace will only be supported for one Major version of PHPFUI, so it is recommended you migrate off of it in a timely manor.

Full Class Documentation

PHPFUI/InstaDoc

Live Examples

Via PHPFUI/Examples

Unit Testing

Full unit testing using phpfui/html-unit-tester

License

PHPFUI is distributed under the MIT License.

PHP Versions

This library only supports modern versions of PHP which still receive security updates. While we would love to support PHP from the late Ming Dynasty, the advantages of modern PHP versions far out weigh quaint notions of backward compatibility. Time to upgrade.


PHPFUI\InstaDoc Library Tests Latest Packagist release

A quick and easy way to add documentation to your PHP project

We all document our code with PHP DocBlocks but we never seem to actually generate the documentation and add it to our project. Why? It simply takes too much time (over a minute), so we put it off till later, and later never comes.

But with PHPFUI/InstaDoc, you can document your site in about a minute (OK, maybe 2). The steps involved:

  • Install PHPFUI/InstaDoc via Composer (30 seconds)
  • Run installation script (30 seconds)
  • Create document page (1 minute, 6 lines of code)

Two minutes to usable documentation with the following features:

PHPFUI/InstaDoc Features

  • Always up to date, even with code that is not yet checked in.
  • Send constructor information including parameters and default values to clipboard.
  • Child and Parent class hierarchy clearly displayed and accessable.
  • Quick access to highlighted PHP source with user selectable highlighting.
  • Quick access to the file's git history for the local repo.
  • Full support for @inheritDoc tag so child method docs are displayed correctly.
  • Documents all projects loaded via Composer automatically.
  • Tabbed documentation so you are not looking at irrelevant methods.
  • Alphabetized everything, no more searching unalphabetized pages!
  • Support for markdown and custom markdown pages.
  • Ability to generate static html files for high volume sites.
  • Add any local repo directories.
  • Remove any Composer project you don't care about.
  • 5+ line config compatible with all PHP frameworks, or standalone.
  • Uses Foundation CSS framework for a great experience on mobile.

Install PHPFUI/InstaDoc (requires PHP >= 7.4)

composer require phpfui/InstaDoc

Run Installation Script

Once installed, you need to run an installation script to copy static files to your public directory. From your project root, run the following:

php vendor/phpfui/instadoc/install.php yourPublicDirectory/subDirectory

Example: php vendor/phpfui/instadoc/install.php public/PHPFUI will add all needed files to public/PHPFUI, which will avoid any conflicts with your current files. You can specify any directory by using \PHPFUI\Page::setResourcePath, but PHPFUI is recomended to keep things simple.

Create Document Page

PHPFUI/InstaDoc does not reply on any framework and can run on a standalone page. It is recommended that you do not make your documentation public, as PHPFUI/InstaDoc will display PHP source files. How you restrict access to the page is up to you. The following does not restrict access and is simply an example:

<?php
include 'yourAutoLoader.php';

// pass the directory containing your composer.json file
$fileManager = new \PHPFUI\InstaDoc\FileManager('../');

// add your App class tree in, pass true as the last parameter if this namespace is in your local git repo.
$fileManager->addNamespace('App', '../App', true);

// load your cached files
$fileManager->load();

// load child classes if you want to display them, if you don't do this step, docs will not show classes that extend the displayed class
\PHPFUI\InstaDoc\ChildClasses::load();

// get the controller
$controller = new \PHPFUI\InstaDoc\Controller($fileManager);

// display will return a fully formed page
echo $controller->display();

That is it. You are done!

Adding New Classes

PHPFUI/InstaDoc saves the classes to display in PHP serialized files. Delete those files (.serial extension) when you want to display new classes. PHPFUI/InstaDoc will regenerate automatically if the files are missing.

Add Child Classes to the Docs

\PHPFUI\InstaDoc\ChildClasses::load('../ChildClasses.serial');

Add a Global Namespace Class

The git repo path defaults to the composer directory, but you can change the path by calling:

$fileManager->addGlobalNameSpaceClass(__DIR__ . '/global/FPDF.php');

Removing a Namespace

$fileManager->excludeNamespace('Carbon');

Add git Repository Page

The git repo path defaults to the composer directory, but you can change the path by calling:

$controller->setGitRoot(getcwd() . '/../');

Add Documents To Your Docs Home Page

$controller->addHomePageMarkdown('../PHPFUI/InstaDoc/README.md');

Set Your Home Page

You may want users to get back into your system easily. Clicking on the top left menu bar will take them here:

$controller->setHomeUrl('/');

Breakup Your Documentation Into Sections

If you have a lot of source code, you might want to break it into sections, so you will need a separate file to store the index in per section:

$fileManager->setBaseFile('SubProject');

Generate Static Files

Just the doc and file pages, no git!

$controller->generate('static/file/path', [\PHPFUI\InstaDoc\Controller::DOC_PAGE, \PHPFUI\InstaDoc\Controller::FILE_PAGE, ]));

Examples and Full Class Documentation

PHPFUI/InstaDoc

Package Documentation

  • \cebe\markdown Readme

    A super fast, highly extensible markdown parser for PHP

    Latest Stable Version
    Total Downloads
    Build Status
    Code Coverage
    Scrutinizer Quality Score

    What is this?

    A set of PHP classes, each representing a Markdown flavor, and a command line tool
    for converting markdown files to HTML files.

    The implementation focus is to be fast (see benchmark) and extensible.
    Parsing Markdown to HTML is as simple as calling a single method (see Usage) providing a solid implementation
    that gives most expected results even in non-trivial edge cases.

    Extending the Markdown language with new elements is as simple as adding a new method to the class that converts the
    markdown text to the expected output in HTML. This is possible without dealing with complex and error prone regular expressions.
    It is also possible to hook into the markdown structure and add elements or read meta information using the internal representation
    of the Markdown text as an abstract syntax tree (see Extending the language).

    Currently the following markdown flavors are supported:

    Future plans are to support:

    Who is using it?

    Installation

    PHP 5.4 or higher is required to use it.
    It will also run on facebook's hhvm.

    The library uses PHPDoc annotations to determine the markdown elements that should be parsed.
    So in case you are using PHP opcache, make sure
    it does not strip comments.

    Installation is recommended to be done via composer by running:

    composer require cebe/markdown "~1.2.0"
    

    Alternatively you can add the following to the require section in your composer.json manually:

    "cebe/markdown": "~1.2.0"
    

    Run composer update afterwards.

    Note: If you have configured PHP with opcache you need to enable the
    opcache.save_comments option because inline element parsing relies on PHPdoc annotations to find declared elements.

    Usage

    In your PHP project

    To parse your markdown you need only two lines of code. The first one is to choose the markdown flavor as
    one of the following:

    • Traditional Markdown: $parser = new \cebe\markdown\Markdown();
    • Github Flavored Markdown: $parser = new \cebe\markdown\GithubMarkdown();
    • Markdown Extra: $parser = new \cebe\markdown\MarkdownExtra();

    The next step is to call the parse()-method for parsing the text using the full markdown language
    or calling the parseParagraph()-method to parse only inline elements.

    Here are some examples:

    // traditional markdown and parse full text
    $parser = new \cebe\markdown\Markdown();
    echo $parser->parse($markdown);
    
    // use github markdown
    $parser = new \cebe\markdown\GithubMarkdown();
    echo $parser->parse($markdown);
    
    // use markdown extra
    $parser = new \cebe\markdown\MarkdownExtra();
    echo $parser->parse($markdown);
    
    // parse only inline elements (useful for one-line descriptions)
    $parser = new \cebe\markdown\GithubMarkdown();
    echo $parser->parseParagraph($markdown);
    

    You may optionally set one of the following options on the parser object:

    For all Markdown Flavors:

    • $parser->html5 = true to enable HTML5 output instead of HTML4.
    • $parser->keepListStartNumber = true to enable keeping the numbers of ordered lists as specified in the markdown.
      The default behavior is to always start from 1 and increment by one regardless of the number in markdown.

    For GithubMarkdown:

    • $parser->enableNewlines = true to convert all newlines to <br/>-tags. By default only newlines with two preceding spaces are converted to <br/>-tags.

    It is recommended to use UTF-8 encoding for the input strings. Other encodings may work, but are currently untested.

    The command line script

    You can use it to render this readme:

    bin/markdown README.md > README.html
    

    Using github flavored markdown:

    bin/markdown --flavor=gfm README.md > README.html
    

    or convert the original markdown description to html using the unix pipe:

    curl http://daringfireball.net/projects/markdown/syntax.text | bin/markdown > md.html
    

    Here is the full Help output you will see when running bin/markdown --help:

    PHP Markdown to HTML converter
    ------------------------------
    
    by Carsten Brandt <mail@cebe.cc>
    
    Usage:
        bin/markdown [--flavor=<flavor>] [--full] [file.md]
    
        --flavor  specifies the markdown flavor to use. If omitted the original markdown by John Gruber [1] will be used.
                  Available flavors:
    
                  gfm   - Github flavored markdown [2]
                  extra - Markdown Extra [3]
    
        --full    ouput a full HTML page with head and body. If not given, only the parsed markdown will be output.
    
        --help    shows this usage information.
    
        If no file is specified input will be read from STDIN.
    
    Examples:
    
        Render a file with original markdown:
    
            bin/markdown README.md > README.html
    
        Render a file using gihtub flavored markdown:
    
            bin/markdown --flavor=gfm README.md > README.html
    
        Convert the original markdown description to html using STDIN:
    
            curl http://daringfireball.net/projects/markdown/syntax.text | bin/markdown > md.html
    
    
    [1] http://daringfireball.net/projects/markdown/syntax
    [2] https://help.github.com/articles/github-flavored-markdown
    [3] http://michelf.ca/projects/php-markdown/extra/
    

    Extensions

    Here are some extensions to this library:

    Extending the language

    Markdown consists of two types of language elements, I'll call them block and inline elements simlar to what you have in
    HTML with <div> and <span>. Block elements are normally spreads over several lines and are separated by blank lines.
    The most basic block element is a paragraph (<p>).
    Inline elements are elements that are added inside of block elements i.e. inside of text.

    This markdown parser allows you to extend the markdown language by changing existing elements behavior and also adding
    new block and inline elements. You do this by extending from the parser class and adding/overriding class methods and
    properties. For the different element types there are different ways to extend them as you will see in the following sections.

    Adding block elements

    The markdown is parsed line by line to identify each non-empty line as one of the block element types.
    To identify a line as the beginning of a block element it calls all protected class methods who's name begins with identify.
    An identify function returns true if it has identified the block element it is responsible for or false if not.
    In the following example we will implement support for fenced code blocks which are part of the github flavored markdown.

    <?php
    
    class MyMarkdown extends \cebe\markdown\Markdown
    {
    	protected function identifyFencedCode($line, $lines, $current)
    	{
    		// if a line starts with at least 3 backticks it is identified as a fenced code block
    		if (strncmp($line, '```', 3) === 0) {
    			return true;
    		}
    		return false;
    	}
    
    	// ...
    }
    

    In the above, $line is a string containing the content of the current line and is equal to $lines[$current].
    You may use $lines and $current to check other lines than the current line. In most cases you can ignore these parameters.

    Parsing of a block element is done in two steps:

    1. Consuming all the lines belonging to it. In most cases this is iterating over the lines starting from the identified
      line until a blank line occurs. This step is implemented by a method named consume{blockName}() where {blockName}
      is the same name as used for the identify function above. The consume method also takes the lines array
      and the number of the current line. It will return two arguments: an array representing the block element in the abstract syntax tree
      of the markdown document and the line number to parse next. In the abstract syntax array the first element refers to the name of
      the element, all other array elements can be freely defined by yourself.
      In our example we will implement it like this:

      protected function consumeFencedCode($lines, $current)
      {
      	// create block array
      	$block = [
      		'fencedCode',
      		'content' => [],
      	];
      	$line = rtrim($lines[$current]);
      
      	// detect language and fence length (can be more than 3 backticks)
      	$fence = substr($line, 0, $pos = strrpos($line, '`') + 1);
      	$language = substr($line, $pos);
      	if (!empty($language)) {
      		$block['language'] = $language;
      	}
      
      	// consume all lines until ```
      	for($i = $current + 1, $count = count($lines); $i < $count; $i++) {
      		if (rtrim($line = $lines[$i]) !== $fence) {
      			$block['content'][] = $line;
      		} else {
      			// stop consuming when code block is over
      			break;
      		}
      	}
      	return [$block, $i];
      }
      
    2. Rendering the element. After all blocks have been consumed, they are being rendered using the
      render{elementName}()-method where elementName refers to the name of the element in the abstract syntax tree:

      protected function renderFencedCode($block)
      {
      	$class = isset($block['language']) ? ' class="language-' . $block['language'] . '"' : '';
      	return "<pre><code$class>" . htmlspecialchars(implode("\n", $block['content']) . "\n", ENT_NOQUOTES, 'UTF-8') . '</code></pre>';
      }
      

      You may also add code highlighting here. In general it would also be possible to render ouput in a different language than
      HTML for example LaTeX.

    Adding inline elements

    Adding inline elements is different from block elements as they are parsed using markers in the text.
    An inline element is identified by a marker that marks the beginning of an inline element (e.g. [ will mark a possible
    beginning of a link or ` will mark inline code).

    Parsing methods for inline elements are also protected and identified by the prefix parse. Additionally a @marker annotation
    in PHPDoc is needed to register the parse function for one or multiple markers.
    The method will then be called when a marker is found in the text. As an argument it takes the text starting at the position of the marker.
    The parser method will return an array containing the element of the abstract sytnax tree and an offset of text it has
    parsed from the input markdown. All text up to this offset will be removed from the markdown before the next marker will be searched.

    As an example, we will add support for the strikethrough feature of github flavored markdown:

    <?php
    
    class MyMarkdown extends \cebe\markdown\Markdown
    {
    	/**
    	 * @marker ~~
    	 */
    	protected function parseStrike($markdown)
    	{
    		// check whether the marker really represents a strikethrough (i.e. there is a closing ~~)
    		if (preg_match('/^~~(.+?)~~/', $markdown, $matches)) {
    			return [
    			    // return the parsed tag as an element of the abstract syntax tree and call `parseInline()` to allow
    			    // other inline markdown elements inside this tag
    				['strike', $this->parseInline($matches[1])],
    				// return the offset of the parsed text
    				strlen($matches[0])
    			];
    		}
    		// in case we did not find a closing ~~ we just return the marker and skip 2 characters
    		return [['text', '~~'], 2];
    	}
    
    	// rendering is the same as for block elements, we turn the abstract syntax array into a string.
    	protected function renderStrike($element)
    	{
    		return '<del>' . $this->renderAbsy($element[1]) . '</del>';
    	}
    }
    

    Composing your own Markdown flavor

    This markdown library is composed of traits so it is very easy to create your own markdown flavor by adding and/or removing
    the single feature traits.

    Designing your Markdown flavor consists of four steps:

    1. Select a base class
    2. Select language feature traits
    3. Define escapeable characters
    4. Optionally add custom rendering behavior

    Select a base class

    If you want to extend from a flavor and only add features you can use one of the existing classes
    (Markdown, GithubMarkdown or MarkdownExtra) as your flavors base class.

    If you want to define a subset of the markdown language, i.e. remove some of the features, you have to
    extend your class from Parser.

    Select language feature traits

    The following shows the trait selection for traditional Markdown.

    class MyMarkdown extends Parser
    {
    	// include block element parsing using traits
    	use block\CodeTrait;
    	use block\HeadlineTrait;
    	use block\HtmlTrait {
    		parseInlineHtml as private;
    	}
    	use block\ListTrait {
    		// Check Ul List before headline
    		identifyUl as protected identifyBUl;
    		consumeUl as protected consumeBUl;
    	}
    	use block\QuoteTrait;
    	use block\RuleTrait {
    		// Check Hr before checking lists
    		identifyHr as protected identifyAHr;
    		consumeHr as protected consumeAHr;
    	}
    	// include inline element parsing using traits
    	use inline\CodeTrait;
    	use inline\EmphStrongTrait;
    	use inline\LinkTrait;
    
    	/**
    	 * @var boolean whether to format markup according to HTML5 spec.
    	 * Defaults to `false` which means that markup is formatted as HTML4.
    	 */
    	public $html5 = false;
    
    	protected function prepare()
    	{
    		// reset references
    		$this->references = [];
    	}
    
    	// ...
    }
    

    In general, just adding the trait with use is enough, however in some cases some fine tuning is desired
    to get most expected parsing results. Elements are detected in alphabetical order of their identification
    function. This means that if a line starting with - could be a list or a horizontal rule, the preference has to be set
    by renaming the identification function. This is what is done with renaming identifyHr to identifyAHr
    and identifyBUl to identifyBUl. The consume function always has to have the same name as the identification function
    so this has to be renamed too.

    There is also a conflict for parsing of the < character. This could either be a link/email enclosed in < and >
    or an inline HTML tag. In order to resolve this conflict when adding the LinkTrait, we need to hide the parseInlineHtml
    method of the HtmlTrait.

    If you use any trait that uses the $html5 property to adjust its output you also need to define this property.

    If you use the link trait it may be useful to implement prepare() as shown above to reset references before
    parsing to ensure you get a reusable object.

    Define escapeable characters

    Depending on the language features you have chosen there is a different set of characters that can be escaped
    using \. The following is the set of escapeable characters for traditional markdown, you can copy it to your class
    as is.

    	/**
    	 * @var array these are "escapeable" characters. When using one of these prefixed with a
    	 * backslash, the character will be outputted without the backslash and is not interpreted
    	 * as markdown.
    	 */
    	protected $escapeCharacters = [
    		'\\', // backslash
    		'`', // backtick
    		'*', // asterisk
    		'_', // underscore
    		'{', '}', // curly braces
    		'[', ']', // square brackets
    		'(', ')', // parentheses
    		'#', // hash mark
    		'+', // plus sign
    		'-', // minus sign (hyphen)
    		'.', // dot
    		'!', // exclamation mark
    		'<', '>',
    	];
    

    Add custom rendering behavior

    Optionally you may also want to adjust rendering behavior by overriding some methods.
    You may refer to the consumeParagraph() method of the Markdown and GithubMarkdown classes for some inspiration
    which define different rules for which elements are allowed to interrupt a paragraph.

    Acknowledgements

    I'd like to thank @erusev for creating Parsedown which heavily influenced this work and provided
    the idea of the line based parsing approach.

    FAQ

    Why another markdown parser?

    While reviewing PHP markdown parsers for choosing one to use bundled with the Yii framework 2.0
    I found that most of the implementations use regex to replace patterns instead
    of doing real parsing. This way extending them with new language elements is quite hard
    as you have to come up with a complex regex, that matches your addition but does not mess
    with other elements. Such additions are very common as you see on github which supports referencing
    issues, users and commits in the comments.
    A real parser should use context aware methods that walk trough the text and
    parse the tokens as they find them. The only implentation that I have found that uses
    this approach is Parsedown which also shows that this implementation is much faster
    than the regex way. Parsedown however is an implementation that focuses on speed and implements
    its own flavor (mainly github flavored markdown) in one class and at the time of this writing was
    not easily extensible.

    Given the situation above I decided to start my own implementation using the parsing approach
    from Parsedown and making it extensible creating a class for each markdown flavor that extend each
    other in the way that also the markdown languages extend each other.
    This allows you to choose between markdown language flavors and also provides a way to compose your
    own flavor picking the best things from all.
    I chose this approach as it is easier to implement and also more intuitive approach compared
    to using callbacks to inject functionallity into the parser.

    Where do I report bugs or rendering issues?

    Just open an issue on github, post your markdown code and describe the problem. You may also attach screenshots of the rendered HTML result to describe your problem.

    How can I contribute to this library?

    Check the CONTRIBUTING.md file for more info.

    Am I free to use this?

    This library is open source and licensed under the MIT License. This means that you can do whatever you want
    with it as long as you mention my name and include the license file. Check the license for details.

    Contact

    Feel free to contact me using email or twitter.

  • \Composer\Semver Readme

    composer/semver

    Semver (Semantic Versioning) library that offers utilities, version constraint parsing and validation.

    Originally written as part of composer/composer,
    now extracted and made available as a stand-alone library.

    Continuous Integration

    Installation

    Install the latest version with:

    $ composer require composer/semver
    

    Requirements

    • PHP 5.3.2 is required but using the latest version of PHP is highly recommended.

    Version Comparison

    For details on how versions are compared, refer to the Versions
    article in the documentation section of the getcomposer.org website.

    Basic usage

    Comparator

    The Composer\Semver\Comparator class provides the following methods for comparing versions:

    • greaterThan($v1, $v2)
    • greaterThanOrEqualTo($v1, $v2)
    • lessThan($v1, $v2)
    • lessThanOrEqualTo($v1, $v2)
    • equalTo($v1, $v2)
    • notEqualTo($v1, $v2)

    Each function takes two version strings as arguments and returns a boolean. For example:

    use Composer\Semver\Comparator;
    
    Comparator::greaterThan('1.25.0', '1.24.0'); // 1.25.0 > 1.24.0
    

    Semver

    The Composer\Semver\Semver class provides the following methods:

    • satisfies($version, $constraints)
    • satisfiedBy(array $versions, $constraint)
    • sort($versions)
    • rsort($versions)

    Intervals

    The Composer\Semver\Intervals static class provides
    a few utilities to work with complex constraints or read version intervals from a constraint:

    use Composer\Semver\Intervals;
    
    // Checks whether $candidate is a subset of $constraint
    Intervals::isSubsetOf(ConstraintInterface $candidate, ConstraintInterface $constraint);
    
    // Checks whether $a and $b have any intersection, equivalent to $a->matches($b)
    Intervals::haveIntersections(ConstraintInterface $a, ConstraintInterface $b);
    
    // Optimizes a complex multi constraint by merging all intervals down to the smallest
    // possible multi constraint. The drawbacks are this is not very fast, and the resulting
    // multi constraint will have no human readable prettyConstraint configured on it
    Intervals::compactConstraint(ConstraintInterface $constraint);
    
    // Creates an array of numeric intervals and branch constraints representing a given constraint
    Intervals::get(ConstraintInterface $constraint);
    
    // Clears the memoization cache when you are done processing constraints
    Intervals::clear()
    

    See the class docblocks for more details.

    License

    composer/semver is licensed under the MIT License, see the LICENSE file for details.

  • \DeepCopy Readme

    DeepCopy

    DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph.

    Total Downloads
    Integrate

    Table of Contents

    1. How
    2. Why
      1. Using simply clone
      2. Overriding __clone()
      3. With DeepCopy
    3. How it works
    4. Going further
      1. Matchers
        1. Property name
        2. Specific property
        3. Type
      2. Filters
        1. SetNullFilter
        2. KeepFilter
        3. DoctrineCollectionFilter
        4. DoctrineEmptyCollectionFilter
        5. DoctrineProxyFilter
        6. ReplaceFilter
        7. ShallowCopyFilter
    5. Edge cases
    6. Contributing
      1. Tests

    How?

    Install with Composer:

    composer require myclabs/deep-copy
    

    Use it:

    use DeepCopy\DeepCopy;
    
    $copier = new DeepCopy();
    $myCopy = $copier->copy($myObject);
    

    Why?

    • How do you create copies of your objects?
    $myCopy = clone $myObject;
    
    • How do you create deep copies of your objects (i.e. copying also all the objects referenced in the properties)?

    You use __clone() and implement the behavior
    yourself.

    • But how do you handle cycles in the association graph?

    Now you're in for a big mess :(

    association graph

    Using simply clone

    Using clone

    Overriding __clone()

    Overriding __clone

    With DeepCopy

    With DeepCopy

    How it works

    DeepCopy recursively traverses all the object's properties and clones them. To avoid cloning the same object twice it
    keeps a hash map of all instances and thus preserves the object graph.

    To use it:

    use function DeepCopy\deep_copy;
    
    $copy = deep_copy($var);
    

    Alternatively, you can create your own DeepCopy instance to configure it differently for example:

    use DeepCopy\DeepCopy;
    
    $copier = new DeepCopy(true);
    
    $copy = $copier->copy($var);
    

    You may want to roll your own deep copy function:

    namespace Acme;
    
    use DeepCopy\DeepCopy;
    
    function deep_copy($var)
    {
        static $copier = null;
        
        if (null === $copier) {
            $copier = new DeepCopy(true);
        }
        
        return $copier->copy($var);
    }
    

    Going further

    You can add filters to customize the copy process.

    The method to add a filter is DeepCopy\DeepCopy::addFilter($filter, $matcher),
    with $filter implementing DeepCopy\Filter\Filter
    and $matcher implementing DeepCopy\Matcher\Matcher.

    We provide some generic filters and matchers.

    Matchers

    • DeepCopy\Matcher applies on a object attribute.
    • DeepCopy\TypeMatcher applies on any element found in graph, including array elements.

    Property name

    The PropertyNameMatcher will match a property by its name:

    use DeepCopy\Matcher\PropertyNameMatcher;
    
    // Will apply a filter to any property of any objects named "id"
    $matcher = new PropertyNameMatcher('id');
    

    Specific property

    The PropertyMatcher will match a specific property of a specific class:

    use DeepCopy\Matcher\PropertyMatcher;
    
    // Will apply a filter to the property "id" of any objects of the class "MyClass"
    $matcher = new PropertyMatcher('MyClass', 'id');
    

    Type

    The TypeMatcher will match any element by its type (instance of a class or any value that could be parameter of
    gettype() function):

    use DeepCopy\TypeMatcher\TypeMatcher;
    
    // Will apply a filter to any object that is an instance of Doctrine\Common\Collections\Collection
    $matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
    

    Filters

    • DeepCopy\Filter applies a transformation to the object attribute matched by DeepCopy\Matcher
    • DeepCopy\TypeFilter applies a transformation to any element matched by DeepCopy\TypeMatcher

    SetNullFilter (filter)

    Let's say for example that you are copying a database record (or a Doctrine entity), so you want the copy not to have
    any ID:

    use DeepCopy\DeepCopy;
    use DeepCopy\Filter\SetNullFilter;
    use DeepCopy\Matcher\PropertyNameMatcher;
    
    $object = MyClass::load(123);
    echo $object->id; // 123
    
    $copier = new DeepCopy();
    $copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
    
    $copy = $copier->copy($object);
    
    echo $copy->id; // null
    

    KeepFilter (filter)

    If you want a property to remain untouched (for example, an association to an object):

    use DeepCopy\DeepCopy;
    use DeepCopy\Filter\KeepFilter;
    use DeepCopy\Matcher\PropertyMatcher;
    
    $copier = new DeepCopy();
    $copier->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category'));
    
    $copy = $copier->copy($object);
    // $copy->category has not been touched
    

    DoctrineCollectionFilter (filter)

    If you use Doctrine and want to copy an entity, you will need to use the DoctrineCollectionFilter:

    use DeepCopy\DeepCopy;
    use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
    use DeepCopy\Matcher\PropertyTypeMatcher;
    
    $copier = new DeepCopy();
    $copier->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
    
    $copy = $copier->copy($object);
    

    DoctrineEmptyCollectionFilter (filter)

    If you use Doctrine and want to copy an entity who contains a Collection that you want to be reset, you can use the
    DoctrineEmptyCollectionFilter

    use DeepCopy\DeepCopy;
    use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
    use DeepCopy\Matcher\PropertyMatcher;
    
    $copier = new DeepCopy();
    $copier->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
    
    $copy = $copier->copy($object);
    
    // $copy->myProperty will return an empty collection
    

    DoctrineProxyFilter (filter)

    If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a
    Doctrine proxy class (...\__CG__\Proxy).
    You can use the DoctrineProxyFilter to load the actual entity behind the Doctrine proxy class.
    Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded
    before other filters are applied!

    use DeepCopy\DeepCopy;
    use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
    use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
    
    $copier = new DeepCopy();
    $copier->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
    
    $copy = $copier->copy($object);
    
    // $copy should now contain a clone of all entities, including those that were not yet fully loaded.
    

    ReplaceFilter (type filter)

    1. If you want to replace the value of a property:
    use DeepCopy\DeepCopy;
    use DeepCopy\Filter\ReplaceFilter;
    use DeepCopy\Matcher\PropertyMatcher;
    
    $copier = new DeepCopy();
    $callback = function ($currentValue) {
      return $currentValue . ' (copy)'
    };
    $copier->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title'));
    
    $copy = $copier->copy($object);
    
    // $copy->title will contain the data returned by the callback, e.g. 'The title (copy)'
    
    1. If you want to replace whole element:
    use DeepCopy\DeepCopy;
    use DeepCopy\TypeFilter\ReplaceFilter;
    use DeepCopy\TypeMatcher\TypeMatcher;
    
    $copier = new DeepCopy();
    $callback = function (MyClass $myClass) {
      return get_class($myClass);
    };
    $copier->addTypeFilter(new ReplaceFilter($callback), new TypeMatcher('MyClass'));
    
    $copy = $copier->copy([new MyClass, 'some string', new MyClass]);
    
    // $copy will contain ['MyClass', 'some string', 'MyClass']
    

    The $callback parameter of the ReplaceFilter constructor accepts any PHP callable.

    ShallowCopyFilter (type filter)

    Stop DeepCopy from recursively copying element, using standard clone instead:

    use DeepCopy\DeepCopy;
    use DeepCopy\TypeFilter\ShallowCopyFilter;
    use DeepCopy\TypeMatcher\TypeMatcher;
    use Mockery as m;
    
    $this->deepCopy = new DeepCopy();
    $this->deepCopy->addTypeFilter(
    	new ShallowCopyFilter,
    	new TypeMatcher(m\MockInterface::class)
    );
    
    $myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDependency2::class));
    // All mocks will be just cloned, not deep copied
    

    Edge cases

    The following structures cannot be deep-copied with PHP Reflection. As a result they are shallow cloned and filters are
    not applied. There is two ways for you to handle them:

    • Implement your own __clone() method
    • Use a filter with a type matcher

    Contributing

    DeepCopy is distributed under the MIT license.

    Tests

    Running the tests is simple:

    vendor/bin/phpunit
    

    Support

    Get professional support via the Tidelift Subscription.

  • \Gitonomy\Git Readme

    Gitlib for Gitonomy

    Build Status
    StyleCI
    License
    Downloads

    This library provides methods to access Git repository from PHP 5.6+.

    It makes shell calls, which makes it less performant than any solution.

    Anyway, it's convenient and don't need to build anything to use it. That's how we love it.

    Quick Start

    You can install gitlib using Composer. Simply require the version you need:

    $ composer require gitonomy/gitlib
    

    or edit your composer.json file by hand:

    {
        "require": {
            "gitonomy/gitlib": "^1.3"
        }
    }
    

    Example Usage

    <?php
    
    use Gitonomy\Git\Repository;
    
    $repository = new Repository('/path/to/repository');
    
    foreach ($repository->getReferences()->getBranches() as $branch) {
        echo '- '.$branch->getName().PHP_EOL;
    }
    
    $repository->run('fetch', ['--all']);
    

    API Documentation

    For Enterprise

    Available as part of the Tidelift Subscription

    The maintainers of gitonomy/gitlib and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

  • \GuzzleHttp Readme

    Guzzle

    Guzzle, PHP HTTP client

    Latest Version
    Build Status
    Total Downloads

    Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
    trivial to integrate with web services.

    • Simple interface for building query strings, POST requests, streaming large
      uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
      etc...
    • Can send both synchronous and asynchronous requests using the same interface.
    • Uses PSR-7 interfaces for requests, responses, and streams. This allows you
      to utilize other PSR-7 compatible libraries with Guzzle.
    • Supports PSR-18 allowing interoperability between other PSR-18 HTTP Clients.
    • Abstracts away the underlying HTTP transport, allowing you to write
      environment and transport agnostic code; i.e., no hard dependency on cURL,
      PHP streams, sockets, or non-blocking event loops.
    • Middleware system allows you to augment and compose client behavior.
    $client = new \GuzzleHttp\Client();
    $response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');
    
    echo $response->getStatusCode(); // 200
    echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8'
    echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}'
    
    // Send an asynchronous request.
    $request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
    $promise = $client->sendAsync($request)->then(function ($response) {
        echo 'I completed! ' . $response->getBody();
    });
    
    $promise->wait();
    

    Help and docs

    We use GitHub issues only to discuss bugs and new features. For support please refer to:

    Installing Guzzle

    The recommended way to install Guzzle is through
    Composer.

    composer require guzzlehttp/guzzle
    

    Version Guidance

    Version Status Packagist Namespace Repo Docs PSR-7 PHP Version
    3.x EOL guzzle/guzzle Guzzle v3 v3 No >=5.3.3,<7.0
    4.x EOL guzzlehttp/guzzle GuzzleHttp v4 N/A No >=5.4,<7.0
    5.x EOL guzzlehttp/guzzle GuzzleHttp v5 v5 No >=5.4,<7.4
    6.x Security fixes guzzlehttp/guzzle GuzzleHttp v6 v6 Yes >=5.5,<8.0
    7.x Latest guzzlehttp/guzzle GuzzleHttp v7 v7 Yes >=7.2.5,<8.2

    Security

    If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see Security Policy for more information.

    License

    Guzzle is made available under the MIT License (MIT). Please see License File for more information.

    For Enterprise

    Available as part of the Tidelift Subscription

    The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

  • \GuzzleHttp\Promise Readme

    Guzzle Promises

    Promises/A+ implementation that handles promise
    chaining and resolution iteratively, allowing for "infinite" promise chaining
    while keeping the stack size constant. Read this blog post
    for a general introduction to promises.

    Features

    • Promises/A+ implementation.
    • Promise resolution and chaining is handled iteratively, allowing for
      "infinite" promise chaining.
    • Promises have a synchronous wait method.
    • Promises can be cancelled.
    • Works with any object that has a then function.
    • C# style async/await coroutine promises using
      GuzzleHttp\Promise\Coroutine::of().

    Quick Start

    A promise represents the eventual result of an asynchronous operation. The
    primary way of interacting with a promise is through its then method, which
    registers callbacks to receive either a promise's eventual value or the reason
    why the promise cannot be fulfilled.

    Callbacks

    Callbacks are registered with the then method by providing an optional
    $onFulfilled followed by an optional $onRejected function.

    use GuzzleHttp\Promise\Promise;
    
    $promise = new Promise();
    $promise->then(
        // $onFulfilled
        function ($value) {
            echo 'The promise was fulfilled.';
        },
        // $onRejected
        function ($reason) {
            echo 'The promise was rejected.';
        }
    );
    

    Resolving a promise means that you either fulfill a promise with a value or
    reject a promise with a reason. Resolving a promise triggers callbacks
    registered with the promise's then method. These callbacks are triggered
    only once and in the order in which they were added.

    Resolving a Promise

    Promises are fulfilled using the resolve($value) method. Resolving a promise
    with any value other than a GuzzleHttp\Promise\RejectedPromise will trigger
    all of the onFulfilled callbacks (resolving a promise with a rejected promise
    will reject the promise and trigger the $onRejected callbacks).

    use GuzzleHttp\Promise\Promise;
    
    $promise = new Promise();
    $promise
        ->then(function ($value) {
            // Return a value and don't break the chain
            return "Hello, " . $value;
        })
        // This then is executed after the first then and receives the value
        // returned from the first then.
        ->then(function ($value) {
            echo $value;
        });
    
    // Resolving the promise triggers the $onFulfilled callbacks and outputs
    // "Hello, reader."
    $promise->resolve('reader.');
    

    Promise Forwarding

    Promises can be chained one after the other. Each then in the chain is a new
    promise. The return value of a promise is what's forwarded to the next
    promise in the chain. Returning a promise in a then callback will cause the
    subsequent promises in the chain to only be fulfilled when the returned promise
    has been fulfilled. The next promise in the chain will be invoked with the
    resolved value of the promise.

    use GuzzleHttp\Promise\Promise;
    
    $promise = new Promise();
    $nextPromise = new Promise();
    
    $promise
        ->then(function ($value) use ($nextPromise) {
            echo $value;
            return $nextPromise;
        })
        ->then(function ($value) {
            echo $value;
        });
    
    // Triggers the first callback and outputs "A"
    $promise->resolve('A');
    // Triggers the second callback and outputs "B"
    $nextPromise->resolve('B');
    

    Promise Rejection

    When a promise is rejected, the $onRejected callbacks are invoked with the
    rejection reason.

    use GuzzleHttp\Promise\Promise;
    
    $promise = new Promise();
    $promise->then(null, function ($reason) {
        echo $reason;
    });
    
    $promise->reject('Error!');
    // Outputs "Error!"
    

    Rejection Forwarding

    If an exception is thrown in an $onRejected callback, subsequent
    $onRejected callbacks are invoked with the thrown exception as the reason.

    use GuzzleHttp\Promise\Promise;
    
    $promise = new Promise();
    $promise->then(null, function ($reason) {
        throw new Exception($reason);
    })->then(null, function ($reason) {
        assert($reason->getMessage() === 'Error!');
    });
    
    $promise->reject('Error!');
    

    You can also forward a rejection down the promise chain by returning a
    GuzzleHttp\Promise\RejectedPromise in either an $onFulfilled or
    $onRejected callback.

    use GuzzleHttp\Promise\Promise;
    use GuzzleHttp\Promise\RejectedPromise;
    
    $promise = new Promise();
    $promise->then(null, function ($reason) {
        return new RejectedPromise($reason);
    })->then(null, function ($reason) {
        assert($reason === 'Error!');
    });
    
    $promise->reject('Error!');
    

    If an exception is not thrown in a $onRejected callback and the callback
    does not return a rejected promise, downstream $onFulfilled callbacks are
    invoked using the value returned from the $onRejected callback.

    use GuzzleHttp\Promise\Promise;
    
    $promise = new Promise();
    $promise
        ->then(null, function ($reason) {
            return "It's ok";
        })
        ->then(function ($value) {
            assert($value === "It's ok");
        });
    
    $promise->reject('Error!');
    

    Synchronous Wait

    You can synchronously force promises to complete using a promise's wait
    method. When creating a promise, you can provide a wait function that is used
    to synchronously force a promise to complete. When a wait function is invoked
    it is expected to deliver a value to the promise or reject the promise. If the
    wait function does not deliver a value, then an exception is thrown. The wait
    function provided to a promise constructor is invoked when the wait function
    of the promise is called.

    $promise = new Promise(function () use (&$promise) {
        $promise->resolve('foo');
    });
    
    // Calling wait will return the value of the promise.
    echo $promise->wait(); // outputs "foo"
    

    If an exception is encountered while invoking the wait function of a promise,
    the promise is rejected with the exception and the exception is thrown.

    $promise = new Promise(function () use (&$promise) {
        throw new Exception('foo');
    });
    
    $promise->wait(); // throws the exception.
    

    Calling wait on a promise that has been fulfilled will not trigger the wait
    function. It will simply return the previously resolved value.

    $promise = new Promise(function () { die('this is not called!'); });
    $promise->resolve('foo');
    echo $promise->wait(); // outputs "foo"
    

    Calling wait on a promise that has been rejected will throw an exception. If
    the rejection reason is an instance of \Exception the reason is thrown.
    Otherwise, a GuzzleHttp\Promise\RejectionException is thrown and the reason
    can be obtained by calling the getReason method of the exception.

    $promise = new Promise();
    $promise->reject('foo');
    $promise->wait();
    

    PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'

    Unwrapping a Promise

    When synchronously waiting on a promise, you are joining the state of the
    promise into the current state of execution (i.e., return the value of the
    promise if it was fulfilled or throw an exception if it was rejected). This is
    called "unwrapping" the promise. Waiting on a promise will by default unwrap
    the promise state.

    You can force a promise to resolve and not unwrap the state of the promise
    by passing false to the first argument of the wait function:

    $promise = new Promise();
    $promise->reject('foo');
    // This will not throw an exception. It simply ensures the promise has
    // been resolved.
    $promise->wait(false);
    

    When unwrapping a promise, the resolved value of the promise will be waited
    upon until the unwrapped value is not a promise. This means that if you resolve
    promise A with a promise B and unwrap promise A, the value returned by the
    wait function will be the value delivered to promise B.

    Note: when you do not unwrap the promise, no value is returned.

    Cancellation

    You can cancel a promise that has not yet been fulfilled using the cancel()
    method of a promise. When creating a promise you can provide an optional
    cancel function that when invoked cancels the action of computing a resolution
    of the promise.

    API

    Promise

    When creating a promise object, you can provide an optional $waitFn and
    $cancelFn. $waitFn is a function that is invoked with no arguments and is
    expected to resolve the promise. $cancelFn is a function with no arguments
    that is expected to cancel the computation of a promise. It is invoked when the
    cancel() method of a promise is called.

    use GuzzleHttp\Promise\Promise;
    
    $promise = new Promise(
        function () use (&$promise) {
            $promise->resolve('waited');
        },
        function () {
            // do something that will cancel the promise computation (e.g., close
            // a socket, cancel a database query, etc...)
        }
    );
    
    assert('waited' === $promise->wait());
    

    A promise has the following methods:

    • then(callable $onFulfilled, callable $onRejected) : PromiseInterface

      Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.

    • otherwise(callable $onRejected) : PromiseInterface

      Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.

    • wait($unwrap = true) : mixed

      Synchronously waits on the promise to complete.

      $unwrap controls whether or not the value of the promise is returned for a
      fulfilled promise or if an exception is thrown if the promise is rejected.
      This is set to true by default.

    • cancel()

      Attempts to cancel the promise if possible. The promise being cancelled and
      the parent most ancestor that has not yet been resolved will also be
      cancelled. Any promises waiting on the cancelled promise to resolve will also
      be cancelled.

    • getState() : string

      Returns the state of the promise. One of pending, fulfilled, or
      rejected.

    • resolve($value)

      Fulfills the promise with the given $value.

    • reject($reason)

      Rejects the promise with the given $reason.

    FulfilledPromise

    A fulfilled promise can be created to represent a promise that has been
    fulfilled.

    use GuzzleHttp\Promise\FulfilledPromise;
    
    $promise = new FulfilledPromise('value');
    
    // Fulfilled callbacks are immediately invoked.
    $promise->then(function ($value) {
        echo $value;
    });
    

    RejectedPromise

    A rejected promise can be created to represent a promise that has been
    rejected.

    use GuzzleHttp\Promise\RejectedPromise;
    
    $promise = new RejectedPromise('Error');
    
    // Rejected callbacks are immediately invoked.
    $promise->then(null, function ($reason) {
        echo $reason;
    });
    

    Promise Interoperability

    This library works with foreign promises that have a then method. This means
    you can use Guzzle promises with React promises
    for example. When a foreign promise is returned inside of a then method
    callback, promise resolution will occur recursively.

    // Create a React promise
    $deferred = new React\Promise\Deferred();
    $reactPromise = $deferred->promise();
    
    // Create a Guzzle promise that is fulfilled with a React promise.
    $guzzlePromise = new GuzzleHttp\Promise\Promise();
    $guzzlePromise->then(function ($value) use ($reactPromise) {
        // Do something something with the value...
        // Return the React promise
        return $reactPromise;
    });
    

    Please note that wait and cancel chaining is no longer possible when forwarding
    a foreign promise. You will need to wrap a third-party promise with a Guzzle
    promise in order to utilize wait and cancel functions with foreign promises.

    Event Loop Integration

    In order to keep the stack size constant, Guzzle promises are resolved
    asynchronously using a task queue. When waiting on promises synchronously, the
    task queue will be automatically run to ensure that the blocking promise and
    any forwarded promises are resolved. When using promises asynchronously in an
    event loop, you will need to run the task queue on each tick of the loop. If
    you do not run the task queue, then promises will not be resolved.

    You can run the task queue using the run() method of the global task queue
    instance.

    // Get the global task queue
    $queue = GuzzleHttp\Promise\Utils::queue();
    $queue->run();
    

    For example, you could use Guzzle promises with React using a periodic timer:

    $loop = React\EventLoop\Factory::create();
    $loop->addPeriodicTimer(0, [$queue, 'run']);
    

    TODO: Perhaps adding a futureTick() on each tick would be faster?

    Implementation Notes

    Promise Resolution and Chaining is Handled Iteratively

    By shuffling pending handlers from one owner to another, promises are
    resolved iteratively, allowing for "infinite" then chaining.

    <?php
    require 'vendor/autoload.php';
    
    use GuzzleHttp\Promise\Promise;
    
    $parent = new Promise();
    $p = $parent;
    
    for ($i = 0; $i < 1000; $i++) {
        $p = $p->then(function ($v) {
            // The stack size remains constant (a good thing)
            echo xdebug_get_stack_depth() . ', ';
            return $v + 1;
        });
    }
    
    $parent->resolve(0);
    var_dump($p->wait()); // int(1000)
    
    

    When a promise is fulfilled or rejected with a non-promise value, the promise
    then takes ownership of the handlers of each child promise and delivers values
    down the chain without using recursion.

    When a promise is resolved with another promise, the original promise transfers
    all of its pending handlers to the new promise. When the new promise is
    eventually resolved, all of the pending handlers are delivered the forwarded
    value.

    A Promise is the Deferred

    Some promise libraries implement promises using a deferred object to represent
    a computation and a promise object to represent the delivery of the result of
    the computation. This is a nice separation of computation and delivery because
    consumers of the promise cannot modify the value that will be eventually
    delivered.

    One side effect of being able to implement promise resolution and chaining
    iteratively is that you need to be able for one promise to reach into the state
    of another promise to shuffle around ownership of handlers. In order to achieve
    this without making the handlers of a promise publicly mutable, a promise is
    also the deferred value, allowing promises of the same parent class to reach
    into and modify the private properties of promises of the same type. While this
    does allow consumers of the value to modify the resolution or rejection of the
    deferred, it is a small price to pay for keeping the stack size constant.

    $promise = new Promise();
    $promise->then(function ($value) { echo $value; });
    // The promise is the deferred value, so you can deliver a value to it.
    $promise->resolve('foo');
    // prints "foo"
    

    Upgrading from Function API

    A static API was first introduced in 1.4.0, in order to mitigate problems with
    functions conflicting between global and local copies of the package. The
    function API will be removed in 2.0.0. A migration table has been provided here
    for your convenience:

    Original Function Replacement Method
    queue Utils::queue
    task Utils::task
    promise_for Create::promiseFor
    rejection_for Create::rejectionFor
    exception_for Create::exceptionFor
    iter_for Create::iterFor
    inspect Utils::inspect
    inspect_all Utils::inspectAll
    unwrap Utils::unwrap
    all Utils::all
    some Utils::some
    any Utils::any
    settle Utils::settle
    each Each::of
    each_limit Each::ofLimit
    each_limit_all Each::ofLimitAll
    !is_fulfilled Is::pending
    is_fulfilled Is::fulfilled
    is_rejected Is::rejected
    is_settled Is::settled
    coroutine Coroutine::of

    Security

    If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see Security Policy for more information.

    License

    Guzzle is made available under the MIT License (MIT). Please see License File for more information.

    For Enterprise

    Available as part of the Tidelift Subscription

    The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

  • \GuzzleHttp\Psr7 Readme

    PSR-7 Message Implementation

    This repository contains a full PSR-7
    message implementation, several stream decorators, and some helpful
    functionality like query string parsing.

    CI
    Static analysis

    Stream implementation

    This package comes with a number of stream implementations and stream
    decorators.

    AppendStream

    GuzzleHttp\Psr7\AppendStream

    Reads from multiple streams, one after the other.

    use GuzzleHttp\Psr7;
    
    $a = Psr7\Utils::streamFor('abc, ');
    $b = Psr7\Utils::streamFor('123.');
    $composed = new Psr7\AppendStream([$a, $b]);
    
    $composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
    
    echo $composed; // abc, 123. Above all listen to me.
    

    BufferStream

    GuzzleHttp\Psr7\BufferStream

    Provides a buffer stream that can be written to fill a buffer, and read
    from to remove bytes from the buffer.

    This stream returns a "hwm" metadata value that tells upstream consumers
    what the configured high water mark of the stream is, or the maximum
    preferred size of the buffer.

    use GuzzleHttp\Psr7;
    
    // When more than 1024 bytes are in the buffer, it will begin returning
    // false to writes. This is an indication that writers should slow down.
    $buffer = new Psr7\BufferStream(1024);
    

    CachingStream

    The CachingStream is used to allow seeking over previously read bytes on
    non-seekable streams. This can be useful when transferring a non-seekable
    entity body fails due to needing to rewind the stream (for example, resulting
    from a redirect). Data that is read from the remote stream will be buffered in
    a PHP temp stream so that previously read bytes are cached first in memory,
    then on disk.

    use GuzzleHttp\Psr7;
    
    $original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
    $stream = new Psr7\CachingStream($original);
    
    $stream->read(1024);
    echo $stream->tell();
    // 1024
    
    $stream->seek(0);
    echo $stream->tell();
    // 0
    

    DroppingStream

    GuzzleHttp\Psr7\DroppingStream

    Stream decorator that begins dropping data once the size of the underlying
    stream becomes too full.

    use GuzzleHttp\Psr7;
    
    // Create an empty stream
    $stream = Psr7\Utils::streamFor();
    
    // Start dropping data when the stream has more than 10 bytes
    $dropping = new Psr7\DroppingStream($stream, 10);
    
    $dropping->write('01234567890123456789');
    echo $stream; // 0123456789
    

    FnStream

    GuzzleHttp\Psr7\FnStream

    Compose stream implementations based on a hash of functions.

    Allows for easy testing and extension of a provided stream without needing
    to create a concrete class for a simple extension point.

    
    use GuzzleHttp\Psr7;
    
    $stream = Psr7\Utils::streamFor('hi');
    $fnStream = Psr7\FnStream::decorate($stream, [
        'rewind' => function () use ($stream) {
            echo 'About to rewind - ';
            $stream->rewind();
            echo 'rewound!';
        }
    ]);
    
    $fnStream->rewind();
    // Outputs: About to rewind - rewound!
    

    InflateStream

    GuzzleHttp\Psr7\InflateStream

    Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.

    This stream decorator converts the provided stream to a PHP stream resource,
    then appends the zlib.inflate filter. The stream is then converted back
    to a Guzzle stream resource to be used as a Guzzle stream.

    LazyOpenStream

    GuzzleHttp\Psr7\LazyOpenStream

    Lazily reads or writes to a file that is opened only after an IO operation
    take place on the stream.

    use GuzzleHttp\Psr7;
    
    $stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
    // The file has not yet been opened...
    
    echo $stream->read(10);
    // The file is opened and read from only when needed.
    

    LimitStream

    GuzzleHttp\Psr7\LimitStream

    LimitStream can be used to read a subset or slice of an existing stream object.
    This can be useful for breaking a large file into smaller pieces to be sent in
    chunks (e.g. Amazon S3's multipart upload API).

    use GuzzleHttp\Psr7;
    
    $original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
    echo $original->getSize();
    // >>> 1048576
    
    // Limit the size of the body to 1024 bytes and start reading from byte 2048
    $stream = new Psr7\LimitStream($original, 1024, 2048);
    echo $stream->getSize();
    // >>> 1024
    echo $stream->tell();
    // >>> 0
    

    MultipartStream

    GuzzleHttp\Psr7\MultipartStream

    Stream that when read returns bytes for a streaming multipart or
    multipart/form-data stream.

    NoSeekStream

    GuzzleHttp\Psr7\NoSeekStream

    NoSeekStream wraps a stream and does not allow seeking.

    use GuzzleHttp\Psr7;
    
    $original = Psr7\Utils::streamFor('foo');
    $noSeek = new Psr7\NoSeekStream($original);
    
    echo $noSeek->read(3);
    // foo
    var_export($noSeek->isSeekable());
    // false
    $noSeek->seek(0);
    var_export($noSeek->read(3));
    // NULL
    

    PumpStream

    GuzzleHttp\Psr7\PumpStream

    Provides a read only stream that pumps data from a PHP callable.

    When invoking the provided callable, the PumpStream will pass the amount of
    data requested to read to the callable. The callable can choose to ignore
    this value and return fewer or more bytes than requested. Any extra data
    returned by the provided callable is buffered internally until drained using
    the read() function of the PumpStream. The provided callable MUST return
    false when there is no more data to read.

    Implementing stream decorators

    Creating a stream decorator is very easy thanks to the
    GuzzleHttp\Psr7\StreamDecoratorTrait. This trait provides methods that
    implement Psr\Http\Message\StreamInterface by proxying to an underlying
    stream. Just use the StreamDecoratorTrait and implement your custom
    methods.

    For example, let's say we wanted to call a specific function each time the last
    byte is read from a stream. This could be implemented by overriding the
    read() method.

    use Psr\Http\Message\StreamInterface;
    use GuzzleHttp\Psr7\StreamDecoratorTrait;
    
    class EofCallbackStream implements StreamInterface
    {
        use StreamDecoratorTrait;
    
        private $callback;
    
        public function __construct(StreamInterface $stream, callable $cb)
        {
            $this->stream = $stream;
            $this->callback = $cb;
        }
    
        public function read($length)
        {
            $result = $this->stream->read($length);
    
            // Invoke the callback when EOF is hit.
            if ($this->eof()) {
                call_user_func($this->callback);
            }
    
            return $result;
        }
    }
    

    This decorator could be added to any existing stream and used like so:

    use GuzzleHttp\Psr7;
    
    $original = Psr7\Utils::streamFor('foo');
    
    $eofStream = new EofCallbackStream($original, function () {
        echo 'EOF!';
    });
    
    $eofStream->read(2);
    $eofStream->read(1);
    // echoes "EOF!"
    $eofStream->seek(0);
    $eofStream->read(3);
    // echoes "EOF!"
    

    PHP StreamWrapper

    You can use the GuzzleHttp\Psr7\StreamWrapper class if you need to use a
    PSR-7 stream as a PHP stream resource.

    Use the GuzzleHttp\Psr7\StreamWrapper::getResource() method to create a PHP
    stream from a PSR-7 stream.

    use GuzzleHttp\Psr7\StreamWrapper;
    
    $stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
    $resource = StreamWrapper::getResource($stream);
    echo fread($resource, 6); // outputs hello!
    

    Static API

    There are various static methods available under the GuzzleHttp\Psr7 namespace.

    GuzzleHttp\Psr7\Message::toString

    public static function toString(MessageInterface $message): string

    Returns the string representation of an HTTP message.

    $request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
    echo GuzzleHttp\Psr7\Message::toString($request);
    

    GuzzleHttp\Psr7\Message::bodySummary

    public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null

    Get a short summary of the message body.

    Will return null if the response is not printable.

    GuzzleHttp\Psr7\Message::rewindBody

    public static function rewindBody(MessageInterface $message): void

    Attempts to rewind a message body and throws an exception on failure.

    The body of the message will only be rewound if a call to tell()
    returns a value other than 0.

    GuzzleHttp\Psr7\Message::parseMessage

    public static function parseMessage(string $message): array

    Parses an HTTP message into an associative array.

    The array contains the "start-line" key containing the start line of
    the message, "headers" key containing an associative array of header
    array values, and a "body" key containing the body of the message.

    GuzzleHttp\Psr7\Message::parseRequestUri

    public static function parseRequestUri(string $path, array $headers): string

    Constructs a URI for an HTTP request message.

    GuzzleHttp\Psr7\Message::parseRequest

    public static function parseRequest(string $message): Request

    Parses a request message string into a request object.

    GuzzleHttp\Psr7\Message::parseResponse

    public static function parseResponse(string $message): Response

    Parses a response message string into a response object.

    GuzzleHttp\Psr7\Header::parse

    public static function parse(string|array $header): array

    Parse an array of header values containing ";" separated data into an
    array of associative arrays representing the header key value pair data
    of the header. When a parameter does not contain a value, but just
    contains a key, this function will inject a key with a '' string value.

    GuzzleHttp\Psr7\Header::normalize

    public static function normalize(string|array $header): array

    Converts an array of header values that may contain comma separated
    headers into an array of headers with no comma separated values.

    GuzzleHttp\Psr7\Query::parse

    public static function parse(string $str, int|bool $urlEncoding = true): array

    Parse a query string into an associative array.

    If multiple values are found for the same key, the value of that key
    value pair will become an array. This function does not parse nested
    PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2
    will be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).

    GuzzleHttp\Psr7\Query::build

    public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string

    Build a query string from an array of key value pairs.

    This function can use the return value of parse() to build a query
    string. This function does not modify the provided keys when an array is
    encountered (like http_build_query() would).

    GuzzleHttp\Psr7\Utils::caselessRemove

    public static function caselessRemove(iterable<string> $keys, $keys, array $data): array

    Remove the items given by the keys, case insensitively from the data.

    GuzzleHttp\Psr7\Utils::copyToStream

    public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void

    Copy the contents of a stream into another stream until the given number
    of bytes have been read.

    GuzzleHttp\Psr7\Utils::copyToString

    public static function copyToString(StreamInterface $stream, int $maxLen = -1): string

    Copy the contents of a stream into a string until the given number of
    bytes have been read.

    GuzzleHttp\Psr7\Utils::hash

    public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string

    Calculate a hash of a stream.

    This method reads the entire stream to calculate a rolling hash, based on
    PHP's hash_init functions.

    GuzzleHttp\Psr7\Utils::modifyRequest

    public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface

    Clone and modify a request with the given changes.

    This method is useful for reducing the number of clones needed to mutate
    a message.

    • method: (string) Changes the HTTP method.
    • set_headers: (array) Sets the given headers.
    • remove_headers: (array) Remove the given headers.
    • body: (mixed) Sets the given body.
    • uri: (UriInterface) Set the URI.
    • query: (string) Set the query string value of the URI.
    • version: (string) Set the protocol version.

    GuzzleHttp\Psr7\Utils::readLine

    public static function readLine(StreamInterface $stream, int $maxLength = null): string

    Read a line from the stream up to the maximum allowed buffer length.

    GuzzleHttp\Psr7\Utils::streamFor

    public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface

    Create a new stream based on the input type.

    Options is an associative array that can contain the following keys:

    • metadata: Array of custom metadata.
    • size: Size of the stream.

    This method accepts the following $resource types:

    • Psr\Http\Message\StreamInterface: Returns the value as-is.
    • string: Creates a stream object that uses the given string as the contents.
    • resource: Creates a stream object that wraps the given PHP stream resource.
    • Iterator: If the provided value implements Iterator, then a read-only
      stream object will be created that wraps the given iterable. Each time the
      stream is read from, data from the iterator will fill a buffer and will be
      continuously called until the buffer is equal to the requested read size.
      Subsequent read calls will first read from the buffer and then call next
      on the underlying iterator until it is exhausted.
    • object with __toString(): If the object has the __toString() method,
      the object will be cast to a string and then a stream will be returned that
      uses the string value.
    • NULL: When null is passed, an empty stream object is returned.
    • callable When a callable is passed, a read-only stream object will be
      created that invokes the given callable. The callable is invoked with the
      number of suggested bytes to read. The callable can return any number of
      bytes, but MUST return false when there is no more data to return. The
      stream object that wraps the callable will invoke the callable until the
      number of requested bytes are available. Any additional bytes will be
      buffered and used in subsequent reads.
    $stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
    $stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
    
    $generator = function ($bytes) {
        for ($i = 0; $i < $bytes; $i++) {
            yield ' ';
        }
    }
    
    $stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
    

    GuzzleHttp\Psr7\Utils::tryFopen

    public static function tryFopen(string $filename, string $mode): resource

    Safely opens a PHP stream resource using a filename.

    When fopen fails, PHP normally raises a warning. This function adds an
    error handler that checks for errors and throws an exception instead.

    GuzzleHttp\Psr7\Utils::tryGetContents

    public static function tryGetContents(resource $stream): string

    Safely gets the contents of a given stream.

    When stream_get_contents fails, PHP normally raises a warning. This
    function adds an error handler that checks for errors and throws an
    exception instead.

    GuzzleHttp\Psr7\Utils::uriFor

    public static function uriFor(string|UriInterface $uri): UriInterface

    Returns a UriInterface for the given value.

    This function accepts a string or UriInterface and returns a
    UriInterface for the given value. If the value is already a
    UriInterface, it is returned as-is.

    GuzzleHttp\Psr7\MimeType::fromFilename

    public static function fromFilename(string $filename): string|null

    Determines the mimetype of a file by looking at its extension.

    GuzzleHttp\Psr7\MimeType::fromExtension

    public static function fromExtension(string $extension): string|null

    Maps a file extensions to a mimetype.

    Upgrading from Function API

    The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API was removed in 2.0.0. A migration table has been provided here for your convenience:

    Original Function Replacement Method
    str Message::toString
    uri_for Utils::uriFor
    stream_for Utils::streamFor
    parse_header Header::parse
    normalize_header Header::normalize
    modify_request Utils::modifyRequest
    rewind_body Message::rewindBody
    try_fopen Utils::tryFopen
    copy_to_string Utils::copyToString
    copy_to_stream Utils::copyToStream
    hash Utils::hash
    readline Utils::readLine
    parse_request Message::parseRequest
    parse_response Message::parseResponse
    parse_query Query::parse
    build_query Query::build
    mimetype_from_filename MimeType::fromFilename
    mimetype_from_extension MimeType::fromExtension
    _parse_message Message::parseMessage
    _parse_request_uri Message::parseRequestUri
    get_message_body_summary Message::bodySummary
    _caseless_remove Utils::caselessRemove

    Additional URI Methods

    Aside from the standard Psr\Http\Message\UriInterface implementation in form of the GuzzleHttp\Psr7\Uri class,
    this library also provides additional functionality when working with URIs as static methods.

    URI Types

    An instance of Psr\Http\Message\UriInterface can either be an absolute URI or a relative reference.
    An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
    the base URI. Relative references can be divided into several forms according to
    RFC 3986 Section 4.2:

    • network-path references, e.g. //example.com/path
    • absolute-path references, e.g. /path
    • relative-path references, e.g. subpath

    The following methods can be used to identify the type of the URI.

    GuzzleHttp\Psr7\Uri::isAbsolute

    public static function isAbsolute(UriInterface $uri): bool

    Whether the URI is absolute, i.e. it has a scheme.

    GuzzleHttp\Psr7\Uri::isNetworkPathReference

    public static function isNetworkPathReference(UriInterface $uri): bool

    Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
    termed an network-path reference.

    GuzzleHttp\Psr7\Uri::isAbsolutePathReference

    public static function isAbsolutePathReference(UriInterface $uri): bool

    Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
    termed an absolute-path reference.

    GuzzleHttp\Psr7\Uri::isRelativePathReference

    public static function isRelativePathReference(UriInterface $uri): bool

    Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
    termed a relative-path reference.

    GuzzleHttp\Psr7\Uri::isSameDocumentReference

    public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool

    Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
    fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
    (apart from its fragment) is considered a same-document reference.

    URI Components

    Additional methods to work with URI components.

    GuzzleHttp\Psr7\Uri::isDefaultPort

    public static function isDefaultPort(UriInterface $uri): bool

    Whether the URI has the default port of the current scheme. Psr\Http\Message\UriInterface::getPort may return null
    or the standard port. This method can be used independently of the implementation.

    GuzzleHttp\Psr7\Uri::composeComponents

    public static function composeComponents($scheme, $authority, $path, $query, $fragment): string

    Composes a URI reference string from its various components according to
    RFC 3986 Section 5.3. Usually this method does not need to be called
    manually but instead is used indirectly via Psr\Http\Message\UriInterface::__toString.

    GuzzleHttp\Psr7\Uri::fromParts

    public static function fromParts(array $parts): UriInterface

    Creates a URI from a hash of parse_url components.

    GuzzleHttp\Psr7\Uri::withQueryValue

    public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface

    Creates a new URI with a specific query string value. Any existing query string values that exactly match the
    provided key are removed and replaced with the given key value pair. A value of null will set the query string
    key without a value, e.g. "key" instead of "key=value".

    GuzzleHttp\Psr7\Uri::withQueryValues

    public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface

    Creates a new URI with multiple query string values. It has the same behavior as withQueryValue() but for an
    associative array of key => value.

    GuzzleHttp\Psr7\Uri::withoutQueryValue

    public static function withoutQueryValue(UriInterface $uri, $key): UriInterface

    Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
    provided key are removed.

    Cross-Origin Detection

    GuzzleHttp\Psr7\UriComparator provides methods to determine if a modified URL should be considered cross-origin.

    GuzzleHttp\Psr7\UriComparator::isCrossOrigin

    public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool

    Determines if a modified URL should be considered cross-origin with respect to an original URL.

    Reference Resolution

    GuzzleHttp\Psr7\UriResolver provides methods to resolve a URI reference in the context of a base URI according
    to RFC 3986 Section 5. This is for example also what web browsers
    do when resolving a link in a website based on the current request URI.

    GuzzleHttp\Psr7\UriResolver::resolve

    public static function resolve(UriInterface $base, UriInterface $rel): UriInterface

    Converts the relative URI into a new URI that is resolved against the base URI.

    GuzzleHttp\Psr7\UriResolver::removeDotSegments

    public static function removeDotSegments(string $path): string

    Removes dot segments from a path and returns the new path according to
    RFC 3986 Section 5.2.4.

    GuzzleHttp\Psr7\UriResolver::relativize

    public static function relativize(UriInterface $base, UriInterface $target): UriInterface

    Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():

    (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
    

    One use-case is to use the current request URI as base URI and then generate relative links in your documents
    to reduce the document size or offer self-contained downloadable document archives.

    $base = new Uri('http://example.com/a/b/');
    echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
    echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
    echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
    echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
    

    Normalization and Comparison

    GuzzleHttp\Psr7\UriNormalizer provides methods to normalize and compare URIs according to
    RFC 3986 Section 6.

    GuzzleHttp\Psr7\UriNormalizer::normalize

    public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface

    Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
    This methods adds additional normalizations that can be configured with the $flags parameter which is a bitmask
    of normalizations to apply. The following normalizations are available:

    • UriNormalizer::PRESERVING_NORMALIZATIONS

      Default normalizations which only include the ones that preserve semantics.

    • UriNormalizer::CAPITALIZE_PERCENT_ENCODING

      All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.

      Example: http://example.org/a%c2%b1bhttp://example.org/a%C2%B1b

    • UriNormalizer::DECODE_UNRESERVED_CHARACTERS

      Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
      ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
      not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
      characters by URI normalizers.

      Example: http://example.org/%7Eusern%61me/http://example.org/~username/

    • UriNormalizer::CONVERT_EMPTY_PATH

      Converts the empty path to "/" for http and https URIs.

      Example: http://example.orghttp://example.org/

    • UriNormalizer::REMOVE_DEFAULT_HOST

      Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
      "localhost". All of file:/myfile, file:///myfile, and file://localhost/myfile are equivalent according to
      RFC 3986.

      Example: file://localhost/myfilefile:///myfile

    • UriNormalizer::REMOVE_DEFAULT_PORT

      Removes the default port of the given URI scheme from the URI.

      Example: http://example.org:80/http://example.org/

    • UriNormalizer::REMOVE_DOT_SEGMENTS

      Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
      change the semantics of the URI reference.

      Example: http://example.org/../a/b/../c/./d.htmlhttp://example.org/a/c/d.html

    • UriNormalizer::REMOVE_DUPLICATE_SLASHES

      Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
      and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
      may change the semantics. Encoded slashes (%2F) are not removed.

      Example: http://example.org//foo///bar.htmlhttp://example.org/foo/bar.html

    • UriNormalizer::SORT_QUERY_PARAMETERS

      Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
      significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
      of the URI.

      Example: ?lang=en&article=fred?article=fred&lang=en

    GuzzleHttp\Psr7\UriNormalizer::isEquivalent

    public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool

    Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
    $normalizations bitmask. The method also accepts relative URI references and returns true when they are equivalent.
    This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
    equivalence or difference of relative references does not mean anything.

    Version Guidance

    Version Status PHP Version
    1.x Security fixes >=5.4,<8.1
    2.x Latest ^7.2.5 || ^8.0

    Security

    If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see Security Policy for more information.

    License

    Guzzle is made available under the MIT License (MIT). Please see License File for more information.

    For Enterprise

    Available as part of the Tidelift Subscription

    The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. Learn more.

  • \Highlight Readme

    highlight.php

    Unit Tests
    Latest Packagist release
    Monthly downloads on Packagist

    highlight.php is a server-side syntax highlighter written in PHP that currently supports 185 languages. It's a port of highlight.js by Ivan Sagalaev that makes full use of the language and style definitions of the original JavaScript project.

    This is the README for highlight.php v10, which is currently under development. The latest stable release is the 9.18.x series.

    Table of Contents

    Installation + Setup

    The recommended approach is to install the project through Composer.

    composer require scrivo/highlight.php
    

    If you're not using Composer, ensure that the classes defined in the Highlight namespace can be found either by inclusion or by an autoloader. A trivial autoloader for this purpose is included in this project as Highlight\Autoloader.php

    Composer Version Constraints

    When requiring this project in your composer.json, it is recommended you use the caret version range and use only the major and minor values; i.e. ^9.14.

    It's come to our attention that a lot of tutorials and projects out there are locking themselves into highly specific versions of this project; e.g. "scrivo/highlight.php": "v9.12.0.1". Please do not do this or encourage it. We promise a reliable backward compatibility policy so there's no reason to lock yourself to such a specific version. By doing this, you are preventing yourself or your users from receiving updates to language definitions and bug fixes.

    Usage

    The \Highlight\Highlighter class contains the syntax highlighting functionality. You can choose between two highlighting modes:

    1. explicit mode
    2. automatic language detection mode

    Explicit Mode

    In explicit mode, you must define which language you will be highlighting as.

    // Instantiate the Highlighter.
    $hl = new \Highlight\Highlighter();
    $code = file_get_contents('some_ruby_script.rb');
    
    try {
        // Highlight some code.
        $highlighted = $hl->highlight('ruby', $code);
    
        echo "<pre><code class=\"hljs {$highlighted->language}\">";
        echo $highlighted->value;
        echo "</code></pre>";
    }
    catch (DomainException $e) {
        // This is thrown if the specified language does not exist
    
        echo "<pre><code>";
        echo htmlentities($code);
        echo "</code></pre>";
    }
    

    Automatic Language Detection Mode

    Alternatively you can use the automatic detection mode, which highlights your code with the language the library thinks is best. It is highly recommended you explicitly choose the language or limit the number of languages to automatically detect to reduce the number of inaccuracies.

    Warning: Auto-detection occurs in a brute force fashion and the language with the most accurate result will be selected. This is extremely inefficient as you supply more languages and may not always be 100% accurate if similar languages are configured.

    $hl = new \Highlight\Highlighter();
    $hl->setAutodetectLanguages(array('ruby', 'python', 'perl'));
    
    $highlighted = $hl->highlightAuto(file_get_contents('some_ruby_script.rb'));
    
    echo "<pre><code class=\"hljs {$highlighted->language}\">";
    echo $highlighted->value;
    echo "</code></pre>";
    

    Default Languages

    If no autodetect languages are set in the highlighter, then every language will be used and cause significant performance issues.

    Stylesheets

    The same stylesheets available in the highlight.js project are available in the styles directory of this project and may be included in your own CSS or made accessible to your web server.

    Highlighter Utilities

    The core of the project is loyal port of highlight.js and is available under the main Highlight namespace. A series of convenience functions are provided under the HighlightUtilities namespace to introduce additional functionality without the need for another dependency.

    Available functions:

    Versioning

    This project will follow the same version numbers as the highlight.js project with regards to languages, meaning that a language definition available in highlight.js 9.12.0 will be available in highlight.php 9.12.0. However, there are times where bugs may arise in this project or its translated definition files, so there'll be one more number appended to the version number. For example, version 9.12.0.1 will contain all of the same languages as highlight.js 9.12.0 but also contain fixes solely to this project. This is done so this project can have version bumps without conflicts should highlight.js release version 9.12.1.

    Backward Compatibility Promise

    Despite the fact that the semantic versioning used in this project mirrors that of highlight.js, this project will adhere to Symfony's Backward Compatibility Promise. You can rest assured that there will be no breaking changes during 9.x and any deprecations will be marked with @deprecated and won't be removed until the next major release.

    Some History

    Geert Bergman
    Sep 30, 2013

    JavaScript code highlighting is very convenient and in many cases just what you want to use. Especially for programming blogs I would not advice you to use otherwise. But there are occasions where you're better off with a more 'static' approach, for instance if you want to send highlighted code in an email or for API documents. For this I needed a code highlighting program preferably written in PHP.

    I couldn't found any satisfactory PHP solution so I decided to port one from JavaScript. After some comparison of different highlighting programs based on license, technology, language support highlight.js came out most favorable in my opinion.

    It was my decision not to make a PHP highlighter but to do a port of highlight.js, these are different things. The goal was to make it work exactly as highlight.js to make as much use as possible of the language definitions and CSS files of the original program.

    Happy coding!

    License

    BSD

  • \Html2Text Readme

    Html2Text

    A PHP library for converting HTML to formatted plain text.

    Build Status

    Installing

    composer require html2text/html2text
    

    Basic Usage

    $html = new \Html2Text\Html2Text('Hello, &quot;<b>world</b>&quot;');
    
    echo $html->getText();  // Hello, "WORLD"
    

    History

    This library started life on the blog of Jon Abernathy http://www.chuggnutt.com/html2text

    A number of projects picked up the library and started using it - among those was RoundCube mail. They made a number of updates to it over time to suit their webmail client.

    Now it has been extracted as a standalone library. Hopefully it can be of use to others.

  • \ICalendarOrg Readme

    Zap Calendar iCalendar Library Tests Latest Packagist release

    A modern 7.4 namespaced fork of Zap Calendar iCalendar Library

    The Zap Calendar iCalendar Library is a PHP library for supporting the iCalendar (RFC 5545) standard.

    This PHP library is for reading and writing iCalendar formatted feeds and files. Features of the library include:

    • Read AND write support for iCalendar files
    • Object based creation and manipulation of iCalendar files
    • Supports expansion of RRULE to a list of repeating dates
    • Supports adding timezone info to iCalendar file

    All iCalendar data is stored in a PHP object tree. This allows any property to be added to the iCalendar feed without requiring specialized library function calls. With power comes responsibility. Missing or invalid properties can cause the resulting iCalendar file to be invalid. Visit iCalendar.org to view valid properties and test your feed using the site's iCalendar validator tool.

    Library API documentation can be found at http://icalendar.org/zapcallibdocs and PHPFUI/ICalendarOrg

    See the examples folder for programs that read and write iCalendar files. Best to include the sample files into a file with an active autoloader or include all the classes to run the examples directly.

    Create an ical object using the ZCiCal object:

    $icalobj = new \ICalendarOrg\ZCiCal();
    

    Add an event object:

    $eventobj = new \ICalendarOrg\ZCiCalNode("VEVENT", $icalobj->curnode);
    

    Add a start and end date to the event:

    // add start date
    $eventobj->addNode(new \ICalendarOrg\ZCiCalDataNode("DTSTART:" . \ICalendarOrg\ZCiCal::fromSqlDateTime("2020-01-01 12:00:00")));
    
    // add end date
    $eventobj->addNode(new \ICalendarOrg\ZCiCalDataNode("DTEND:" . \ICalendarOrg\ZCiCal::fromSqlDateTime("2020-01-01 13:00:00")));
    

    Write the object in iCalendar format using the export() function call:

    echo $icalobj->export();
    

    This example will not validate since it is missing some required elements. Look at the simpleevent.php example for the minimum # of elements needed for a validated iCalendar file.

    To create a multi-event iCalendar file, simply create multiple event objects. For example:

    $icalobj = new \ICalendarOrg\ZCiCal();
    $eventobj1 = new \ICalendarOrg\ZCiCalNode("VEVENT", $icalobj->curnode);
    $eventobj1->addNode(new \ICalendarOrg\ZCiCalDataNode("SUMMARY:Event 1"));
    ...
    $eventobj2 = new \ICalendarOrg\ZCiCalNode("VEVENT", $icalobj->curnode);
    $eventobj2->addNode(new \ICalendarOrg\ZCiCalDataNode("SUMMARY:Event 2"));
    ...
    

    To read an existing iCalendar file/feed, create the ZCiCal object with a string representing the contents of the iCalendar file:

    $icalobj = new \ICalendarOrg\ZCiCal($icalstring);
    

    Large iCalendar files can be read in chunks to reduce the amount of memory needed to hold the iCalendar feed in memory. This example reads 500 events at a time:

    $icalobj = null;
    $eventcount = 0;
    $maxevents = 500;
    do
    {
    	$icalobj = new \ICalendarOrg\ZCiCal($icalstring, $maxevents, $eventcount);
    	...
    	$eventcount += $maxevents;
    }
    while($icalobj->countEvents() >= $eventcount);
    

    You can read the events from an imported (or created) iCalendar object in this manner:

    foreach($icalobj->tree->child as $node)
    {
    	if($node->getName() == "VEVENT")
    	{
    		foreach($node->data as $key => $value)
    		{
    			if($key == "SUMMARY")
    			{
    				echo "event title: " . $value->getValues() . "\n";
    			}
    		}
    	}
    }
    

    Known Limitations

    • Since the library utilizes objects to read and write iCalendar data, the size of the iCalendar data is limited to the amount of available memory on the machine. The ZCiCal() object supports reading a range of events to minimize memory space.
    • The library ignores timezone info when importing files, instead utilizing PHP's timezone library for calculations (timezones are supported when exporting files). Imported timezones need to be aliased to a PHP supported timezone.
    • At this time, the library does not support the "BYSETPOS" option in RRULE items.
  • \League\Geotools Readme

    Geotools

    Geotools is a PHP geo-related library, built atop Geocoder and
    React libraries.

    Build Status
    Latest Version
    Total Downloads
    Quality Score
    SensioLabs Insight
    PHP7 Ready

    Features

    • Batch geocode & reverse geocoding request(s) in series / in parallel against one or a
      set of providers. »
    • Cache geocode & reverse geocoding result(s) with PSR-6 to improve performances. »
    • Compute geocode & reverse geocoding in the command-line interface (CLI) + dumpers and formatters. »
    • Accept almost all kind of WGS84
      geographic coordinates as coordinates.
      »
    • Support 23 different ellipsoids and it's easy to provide a new one if needed. »
    • Convert and format decimal degrees coordinates to decimal minutes or degrees minutes seconds coordinates.
      »
    • Convert decimal degrees coordinates in the
      Universal Transverse Mercator
      (UTM) projection. »
    • Compute the distance in meter (by default), km, mi or ft between two coordinates using flat,
      great circle, haversine or vincenty algorithms. »
    • Compute the initial and final bearing from the origin coordinate to the destination coordinate in degrees.
      »
    • Compute the initial and final cardinal point (direction) from the origin coordinate to the destination
      coordinate, read more in wikipedia. »
    • Compute the half-way point (coordinate) between the origin and the destination coordinates. »
    • Compute the destination point (coordinate) with given bearing in degrees and a distance in meters. »
    • Encode a coordinate to a geo hash string and decode it to a coordinate, read more in
      wikipedia and on geohash.org. »
    • Encode a coordinate via the 10:10 algorithm. »
    • Polygon class provides methods to check either a poing (coordinate) is in, or on the polygon's boundaries.
      »
    • A command-line interface (CLI) for Distance, Point, Geohash and Convert classes. »
    • Integration with Frameworks: Laravel 4, Silex ... »
    • ... more to come ...

    Installation

    Geotools can be found on Packagist.
    The recommended way to install Geotools is through composer.

    Run the following on the command line:

    php composer require league/geotools=@stable
    

    Protip: you should browse the
    league/geotools
    page to choose a stable version to use, avoid the @stable meta constraint.

    Important: you should use the 0.4 version if you use Geocoder 2.x or/and PHP 5.3.

    And install dependencies:

    $ curl -sS https://getcomposer.org/installer | php
    $ php composer.phar install
    

    Now you can add the autoloader, and you will have access to the library:

    <?php
    
    require 'vendor/autoload.php';
    

    Usage & API

    Coordinate & Ellipsoid

    The default geodetic datum is WGS84 and coordinates are in
    decimal degrees.

    Here are the available ellipsoids: AIRY, AUSTRALIAN_NATIONAL, BESSEL_1841, BESSEL_1841_NAMBIA,
    CLARKE_1866, CLARKE_1880, EVEREST, FISCHER_1960_MERCURY, FISCHER_1968, GRS_1967, GRS_1980,
    HELMERT_1906, HOUGH, INTERNATIONAL, KRASSOVSKY, MODIFIED_AIRY, MODIFIED_EVEREST,
    MODIFIED_FISCHER_1960, SOUTH_AMERICAN_1969, WGS60, WGS66, WGS72, and WGS84.

    If you need to use an other ellipsoid, just create an array like this:

    <?php
    
    $myEllipsoid = \League\Geotools\Coordinate\Ellipsoid::createFromArray([
        'name' => 'My Ellipsoid', // The name of the Ellipsoid
        'a'    => 123.0, // The semi-major axis (equatorial radius) in meters
        'invF' => 456.0 // The inverse flattening
    ]);
    

    Geotools is built atop Geocoder. It means it's possible to use the
    \Geocoder\Model\Address directly but it's also possible to use a string or a simple array with its
    latitude and longitude.

    It supports valid and acceptable geographic coordinates
    like:

    • 40:26:46N,079:56:55W
    • 40:26:46.302N 079:56:55.903W
    • 40°26′47″N 079°58′36″W
    • 40d 26′ 47″ N 079d 58′ 36″ W
    • 40.446195N 79.948862W
    • 40.446195, -79.948862
    • 40° 26.7717, -79° 56.93172

    Latitudes below -90.0 or above 90.0 degrees are capped through \League\Geotools\Coordinate\Coordinate::normalizeLatitude().
    Longitudes below -180.0 or above 180.0 degrees are wrapped through \League\Geotools\Coordinate\Coordinate::normalizeLongitude().

    <?php
    
    use League\Geotools\Coordinate\Coordinate;
    use League\Geotools\Coordinate\Ellipsoid;
    
    // from an \Geocoder\Model\Address instance within Airy ellipsoid
    $coordinate = new Coordinate($geocoderResult, Ellipsoid::createFromName(Ellipsoid::AIRY));
    // or in an array of latitude/longitude coordinate within GRS 1980 ellipsoid
    $coordinate = new Coordinate([48.8234055, 2.3072664], Ellipsoid::createFromName(Ellipsoid::GRS_1980));
    // or in latitude/longitude coordinate within WGS84 ellipsoid
    $coordinate = new Coordinate('48.8234055, 2.3072664');
    // or in degrees minutes seconds coordinate within WGS84 ellipsoid
    $coordinate = new Coordinate('48°49′24″N, 2°18′26″E');
    // or in decimal minutes coordinate within WGS84 ellipsoid
    $coordinate = new Coordinate('48 49.4N, 2 18.43333E');
    // the result will be:
    printf("Latitude: %F\n", $coordinate->getLatitude()); // 48.8234055
    printf("Longitude: %F\n", $coordinate->getLongitude()); // 2.3072664
    printf("Ellipsoid name: %s\n", $coordinate->getEllipsoid()->getName()); // WGS 84
    printf("Equatorial radius: %F\n", $coordinate->getEllipsoid()->getA()); // 6378136.0
    printf("Polar distance: %F\n", $coordinate->getEllipsoid()->getB()); // 6356751.317598
    printf("Inverse flattening: %F\n", $coordinate->getEllipsoid()->getInvF()); // 298.257224
    printf("Mean radius: %F\n", $coordinate->getEllipsoid()->getArithmeticMeanRadius()); // 6371007.772533
    // it's also possible to modify the coordinate without creating an other coodinate
    $coordinate->setFromString('40°26′47″N 079°58′36″W');
    printf("Latitude: %F\n", $coordinate->getLatitude()); // 40.446388888889
    printf("Longitude: %F\n", $coordinate->getLongitude()); // -79.976666666667
    

    Convert

    It provides methods (and aliases) to convert decimal degrees WGS84 coordinates to degrees minutes seconds
    or decimal minutes WGS84 coordinates. You can format the output string easily.

    You can also convert them in the Universal Transverse Mercator (UTM) projection (Southwest coast of Norway and the
    region of Svalbard are covered).

    <?php
    
    $geotools   = new \League\Geotools\Geotools();
    $coordinate = new \League\Geotools\Coordinate\Coordinate('40.446195, -79.948862');
    $converted  = $geotools->convert($coordinate);
    // convert to decimal degrees without and with format string
    printf("%s\n", $converted->toDecimalMinutes()); // 40 26.7717N, -79 56.93172W
    // convert to degrees minutes seconds without and with format string
    printf("%s\n", $converted->toDegreesMinutesSeconds('<p>%P%D:%M:%S, %p%d:%m:%s</p>')); // <p>40:26:46, -79:56:56</p>
    // convert in the UTM projection (standard format)
    printf("%s\n", $converted->toUniversalTransverseMercator()); // 17T 589138 4477813
    

    Here is the mapping:

    Decimal minutes Latitude Longitude
    Positive or negative sign %P %p
    Direction %L %l
    Degrees %D %d
    Decimal minutes %N %n
    Degrees minutes seconds Latitude Longitude
    Positive or negative sign %P %p
    Direction %L %l
    Degrees %D %d
    Minutes %M %m
    Seconds %S %s

    Batch

    It provides a very handy way to batch geocode and reverse geocoding requests in serie or in parallel against
    a set of providers.
    Thanks to Geocoder and React libraries.

    It's possible to batch one request (a string) or a set of request (an array) against one provider or
    set of providers.

    You can use a provided cache engine or use your own by setting a cache object which should implement
    League\Geotools\Cache\CacheInterface and extend League\Geotools\Cache\AbstractCache if needed.

    At the moment Geotools supports any PSR-6 cache.

    NB: Before you implement caching in your app please be sure that doing so does not violate the Terms of Service
    for your(s) geocoding provider(s).

    <?php
    
    $geocoder = new \Geocoder\ProviderAggregator(); // or \Geocoder\TimedGeocoder
    $httpClient  = HttpClientDiscovery::find();
    
    $geocoder->registerProviders([
        new \Geocoder\Provider\GoogleMaps\GoogleMaps($httpClient),
        new \Geocoder\Provider\OpenStreetMap\OpenStreetMap($httpClient),
        new \Geocoder\Provider\BingMaps\BingMaps($httpClient, '<FAKE_API_KEY>'), // throws InvalidCredentialsException
        new \Geocoder\Provider\Yandex\Yandex($httpClient),
        new \Geocoder\Provider\FreeGeoIp\FreeGeoIp($httpClient),
        new \Geocoder\Provider\Geoip\Geoip(),
    ]);
    
    try {
        $geotools = new \League\Geotools\Geotools();
        $cache    = new \Cache\Adapter\PHPArray\ArrayCachePool();
      
        $results  = $geotools->batch($geocoder)->setCache($cache)->geocode([
            'Paris, France',
            'Copenhagen, Denmark',
            '74.200.247.59',
            '::ffff:66.147.244.214'
        ])->parallel();
    } catch (\Exception $e) {
        die($e->getMessage());
    }
    
    $dumper = new \Geocoder\Dumper\WktDumper();
    foreach ($results as $result) {
        // if a provider throws an exception (UnsupportedException, InvalidCredentialsException ...)
        // an custom /Geocoder/Result/Geocoded instance is returned which embedded the name of the provider,
        // the query string and the exception string. It's possible to use dumpers
        // and/or formatters from the Geocoder library.
        printf("%s|%s|%s\n",
            $result->getProviderName(),
            $result->getQuery(),
            '' == $result->getExceptionMessage() ? $dumper->dump($result) : $result->getExceptionMessage()
        );
    }
    

    You should get 24 results (4 values to geocode against 6 providers) something like:

    google_maps|Paris, France|POINT(2.352222 48.856614)
    google_maps|Copenhagen, Denmark|POINT(12.568337 55.676097)
    google_maps|74.200.247.59|The GoogleMapsProvider does not support IP addresses.
    google_maps|::ffff:66.147.244.214|The GoogleMapsProvider does not support IP addresses.
    openstreetmaps|Paris, France|POINT(2.352133 48.856506)
    openstreetmaps|Copenhagen, Denmark|POINT(12.570072 55.686724)
    openstreetmaps|74.200.247.59|Could not execute query http://nominatim.openstreetmap.org/search?q=74.200.247.59&format=xml&addressdetails=1&limit=1
    openstreetmaps|::ffff:66.147.244.214|The OpenStreetMapProvider does not support IPv6 addresses.
    bing_maps|Paris, France|Could not execute query http://dev.virtualearth.net/REST/v1/Locations/?q=Paris%2C+France&key=<FAKE_API_KEY>
    bing_maps|Copenhagen, Denmark|Could not execute query http://dev.virtualearth.net/REST/v1/Locations/?q=Copenhagen%2C+Denmark&key=<FAKE_API_KEY>
    bing_maps|74.200.247.59|The BingMapsProvider does not support IP addresses.
    bing_maps|::ffff:66.147.244.214|The BingMapsProvider does not support IP addresses.
    yandex|Paris, France|POINT(2.341198 48.856929)
    yandex|Copenhagen, Denmark|POINT(12.567602 55.675682)
    yandex|74.200.247.59|The YandexProvider does not support IP addresses.
    yandex|::ffff:66.147.244.214|The YandexProvider does not support IP addresses.
    free_geo_ip|Paris, France|The FreeGeoIpProvider does not support Street addresses.
    free_geo_ip|Copenhagen, Denmark|The FreeGeoIpProvider does not support Street addresses.
    free_geo_ip|74.200.247.59|POINT(-122.415600 37.748400)
    free_geo_ip|::ffff:66.147.244.214|POINT(-111.613300 40.218100)
    geoip|Paris, France|The GeoipProvider does not support Street addresses.
    geoip|Copenhagen, Denmark|The GeoipProvider does not support Street addresses.
    geoip|74.200.247.59|POINT(-122.415604 37.748402)
    geoip|::ffff:66.147.244.214|The GeoipProvider does not support IPv6 addresses.
    

    Batch reverse geocoding is something like:

    <?php
    
    // ... $geocoder like the previous example ...
    // If you want to reverse one coordinate
    try {
        $results = $geotools->batch($geocoder)->reverse(
            new \League\Geotools\Coordinate\Coordinate([2.307266, 48.823405])
        )->parallel();
    } catch (\Exception $e) {
        die($e->getMessage());
    }
    // Or if you want to reverse geocoding 3 coordinates
    $coordinates = [
        new \League\Geotools\Coordinate\Coordinate([2.307266, 48.823405]),
        new \League\Geotools\Coordinate\Coordinate([12.568337, 55.676097]),
        new \League\Geotools\Coordinate\Coordinate('-74.005973 40.714353')),
    ];
    $results = $geotools->batch($geocoder)->reverse($coordinates)->parallel();
    // ...
    

    If you want to batch it in serie, replace the method parallel() by serie().

    To optimize batch requests you need to register providers according to their capabilities and what you're
    looking for (geocode street addresses, geocode IPv4, geocode IPv6 or reverse geocoding),
    please read more at the Geocoder library doc.

    Distance

    It provides methods to compute the distance in meter (by default), km, mi or ft between two coordinates
    using flat (most performant), great circle, haversine or vincenty (most accurate) algorithms.

    Those coordinates should be in the same ellipsoid.

    <?php
    
    $geotools = new \League\Geotools\Geotools();
    $coordA   = new \League\Geotools\Coordinate\Coordinate([48.8234055, 2.3072664]);
    $coordB   = new \League\Geotools\Coordinate\Coordinate([43.296482, 5.36978]);
    $distance = $geotools->distance()->setFrom($coordA)->setTo($coordB);
    
    printf("%s\n",$distance->flat()); // 659166.50038742 (meters)
    printf("%s\n",$distance->greatCircle()); // 659021.90812846
    printf("%s\n",$distance->in('km')->haversine()); // 659.02190812846
    printf("%s\n",$distance->in('mi')->vincenty()); // 409.05330679648
    printf("%s\n",$distance->in('ft')->flat()); // 2162619.7519272
    

    Point

    It provides methods to compute the initial and final bearing in degrees, the initial and final cardinal direction,
    the middle point and the destination point. The middle and the destination points returns a
    \League\Geotools\Coordinate\Coordinate object with the same ellipsoid.

    <?php
    
    $geotools = new \League\Geotools\Geotools();
    $coordA   = new \League\Geotools\Coordinate\Coordinate([48.8234055, 2.3072664]);
    $coordB   = new \League\Geotools\Coordinate\Coordinate([43.296482, 5.36978]);
    $vertex    =  $geotools->vertex()->setFrom($coordA)->setTo($coordB);
    
    printf("%d\n", $vertex->initialBearing()); // 157 (degrees)
    printf("%s\n", $vertex->initialCardinal()); // SSE (SouthSouthEast)
    printf("%d\n", $vertex->finalBearing()); // 160 (degrees)
    printf("%s\n", $vertex->finalCardinal()); // SSE (SouthSouthEast)
    
    $middlePoint = $vertex->middle(); // \League\Geotools\Coordinate\Coordinate
    printf("%s\n", $middlePoint->getLatitude()); // 46.070143125815
    printf("%s\n", $middlePoint->getLongitude()); // 3.9152401085931
    
    $destinationPoint = $geotools->vertex()->setFrom($coordA)->destination(180, 200000); // \League\Geotools\Coordinate\Coordinate
    printf("%s\n", $destinationPoint->getLatitude()); // 47.026774650075
    printf("%s\n", $destinationPoint->getLongitude()); // 2.3072664
    

    Geohash

    It provides methods to get the geo hash and its bounding box's coordinates (SouthWest & NorthEast)
    of a coordinate and the coordinate and its bounding box's coordinates (SouthWest & NorthEast) of a geo hash.

    <?php
    
    $geotools       = new \League\Geotools\Geotools();
    $coordToGeohash = new \League\Geotools\Coordinate\Coordinate('43.296482, 5.36978');
    
    // encoding
    $encoded = $geotools->geohash()->encode($coordToGeohash, 4); // 12 is the default length / precision
    // encoded
    printf("%s\n", $encoded->getGeohash()); // spey
    // encoded bounding box
    $boundingBox = $encoded->getBoundingBox(); // array of \League\Geotools\Coordinate\CoordinateInterface
    $southWest   = $boundingBox[0];
    $northEast   = $boundingBox[1];
    printf("http://www.openstreetmap.org/?minlon=%s&minlat=%s&maxlon=%s&maxlat=%s&box=yes\n",
        $southWest->getLongitude(), $southWest->getLatitude(),
        $northEast->getLongitude(), $northEast->getLatitude()
    ); // http://www.openstreetmap.org/?minlon=5.2734375&minlat=43.2421875&maxlon=5.625&maxlat=43.41796875&box=yes
    
    // decoding
    $decoded = $geotools->geohash()->decode('spey61y');
    // decoded coordinate
    printf("%s\n", $decoded->getCoordinate()->getLatitude()); // 43.296432495117
    printf("%s\n", $decoded->getCoordinate()->getLongitude()); // 5.3702545166016
    // decoded bounding box
    $boundingBox = $decoded->getBoundingBox(); //array of \League\Geotools\Coordinate\CoordinateInterface
    $southWest   = $boundingBox[0];
    $northEast   = $boundingBox[1];
    printf("http://www.openstreetmap.org/?minlon=%s&minlat=%s&maxlon=%s&maxlat=%s&box=yes\n",
        $southWest->getLongitude(), $southWest->getLatitude(),
        $northEast->getLongitude(), $northEast->getLatitude()
    ); // http://www.openstreetmap.org/?minlon=5.3695678710938&minlat=43.295745849609&maxlon=5.3709411621094&maxlat=43.297119140625&box=yes
    

    10:10

    Represent a location with 10m accuracy using a 10 character code that includes features to prevent errors in
    entering the code. Read more about the algorithm here.

    <?php
    
    $tenten = new \League\Geotools\Tests\Geohash\TenTen;
    $tenten->encode(new Coordinate([51.09559, 1.12207])); // MEQ N6G 7NY5
    

    Vertex

    Represents a segment with a direction.
    You can find if two vertexes are on the same line.

    <?php
    	$vertexA->setFrom(48.8234055);
    	$vertexA->setTo(2.3072664);
    
    	$vertexB->setFrom(48.8234055);
    	$vertexB->setTo(2.3072664);
    	$vertexA->isOnSameLine($vertexB);
    

    Polygon

    It helps you to know if a point (coordinate) is in a Polygon or on the Polygon's boundaries and if this in on
    a Polygon's vertex.

    First you need to create the polygon, you can provide:

    • an array of arrays
    • an array of Coordinate
    • a CoordinateCollection
    <?php
    
    $polygon = new \League\Geotools\Polygon\Polygon([
        [48.9675969, 1.7440796],
        [48.4711003, 2.5268555],
        [48.9279131, 3.1448364],
        [49.3895245, 2.6119995],
    ]);
    
    $polygon->setPrecision(5); // set the comparision precision
    $polygon->pointInPolygon(new \League\Geotools\Coordinate\Coordinate([49.1785607, 2.4444580])); // true
    $polygon->pointInPolygon(new \League\Geotools\Coordinate\Coordinate([49.1785607, 5])); // false
    $polygon->pointOnBoundary(new \League\Geotools\Coordinate\Coordinate([48.7193486, 2.13546755])); // true
    $polygon->pointOnBoundary(new \League\Geotools\Coordinate\Coordinate([47.1587188, 2.87841795])); // false
    $polygon->pointOnVertex(new \League\Geotools\Coordinate\Coordinate([48.4711003, 2.5268555])); // true
    $polygon->pointOnVertex(new \League\Geotools\Coordinate\Coordinate([49.1785607, 2.4444580])); // false
    $polygon->getBoundingBox(); // return the BoundingBox object
    

    CLI

    It provides command lines to compute methods provided by Distance, Point, Geohash and Convert classes.
    Thanks to the Symfony Console Component.

    $ php geotools list // list of available commands
    $ php geotools help distance:flat // get the help
    $ php geotools distance:flat "40° 26.7717, -79° 56.93172" "30°16′57″N 029°48′32″W" // 4690203.1048522
    $ php geotools distance:haversine "35,45" "45,35" --ft  // 4593030.9787593
    $ php geotools distance:vincenty "35,45" "45,35" --km  // 1398.4080717661
    $ php geotools d:v "35,45" "45,35" --km --ellipsoid=WGS60 // 1398.4145201642
    $ php geotools point:initial-cardinal "40:26:46.302N 079:56:55.903W" "43.296482, 5.36978" // NE (NordEast)
    $ php geotools point:final-cardinal "40:26:46.302N 079:56:55.903W" "43.296482, 5.36978" // ESE (EastSouthEast)
    $ php geotools point:destination "40° 26.7717, -79° 56.93172" 25 10000 // 40.527599285543, -79.898914904538
    $ php geotools p:d "40° 26.7717, -79° 56.93172" 25 10000 --ellipsoid=GRS_1980 // 40.527599272782, -79.898914912379
    $ php geotools geohash:encode "40° 26.7717, -79° 56.93172" --length=3 // dpp
    $ php geotools convert:dm "40.446195, -79.948862" --format="%P%D°%N %p%d°%n" // 40°26.7717 -79°56.93172
    $ php geotools convert:dms "40.446195, -79.948862" --format="%P%D:%M:%S, %p%d:%m:%s" // 40:26:46, -79:56:56
    $ php geotools convert:utm "60.3912628, 5.3220544" // 32V 297351 6700644
    $ php geotools c:u "60.3912628, 5.3220544" --ellipsoid=AIRY // 32V 297371 6700131
    ...
    

    Compute street addresses, IPv4s or IPv6s geocoding and reverse geocoding right in your console.

    It's possible to define and precise your request through these options:

    • --provider: bing_maps, yahoo, maxmind... google_maps is the default one. See the full list
      here.
    • --raw: the result output in RAW format, shows Adapter, Provider and Arguments if any.
    • --json: the result output in JSON string format.
    • --args: this option accepts multiple values (e.g. --args="API_KEY" --args="LOCALE") if your provider needs or
      can have arguments.
    • --dumper: this option is available for geocoding, gpx, geojson, kml, wkb and wkt by default.
      Read more here.
    • --format: this option is available for reverse geocoding, see the mapping
      here.
    $ php geotools help geocoder:geocode // get the help
    $ php geotools geocoder:geocode "Copenhagen, Denmark" // 55.6760968, 12.5683371
    $ php geotools geocoder:geocode "74.200.247.59" --provider="free_geo_ip" // 37.7484, -122.4156
    $ php geotools geocoder:geocode Paris --args="fr_FR" --args="France" --args="true" // 48.856614, 2.3522219
    $ php geotools geocoder:geocode Paris --dumper=wkt // POINT(2.352222 48.856614)
    ...
    $ php geotools geocoder:reverse "48.8631507, 2.388911" // Avenue Gambetta 10, 75020 Paris
    $ php geotools geocoder:reverse "48.8631507, 2.388911" --format="%L, %A1, %C" // Paris, Île-De-France, France
    $ php geotools geocoder:reverse "48.8631507, 2.388911" --format="%L, %A1, %C" --provider="openstreetmaps"
    // Paris, Île-De-France, France Métropolitaine
    ...
    $ php geotools geocoder:geocode "Tagensvej 47, Copenhagen" --raw --args=da_DK --args=Denmark
    

    The last command will show an output like this:

    HttpClient:    \Http\Client\Curl\Client
    Provider:      \Geocoder\Provider\GoogleMaps
    Cache:         \League\Geotools\Cache\Redis
    Arguments:     da_DK,Denmark
    ---
    Latitude:      55.699953
    Longitude:     12.552736
    Bounds
     - South: 55.699953
     - West:  12.552736
     - North: 55.699953
     - East:  12.552736
    Street Number: 47
    Street Name:   Tagensvej
    Zipcode:       2200
    City:          Copenhagen
    City District: København N
    County:        København
    County Code:   KØBENHAVN
    Region:        Capital Region Of Denmark
    Region Code:   CAPITAL REGION OF DENMARK
    Country:       Denmark
    Country Code:  DK
    Timezone:
    

    Integration with Frameworks

    Unit Tests

    To run unit tests, you'll need the cURL extension and a set of dependencies, you can install them using Composer:

    $ php composer.phar install --dev
    

    Once installed, just launch the following command:

    $ phpunit --coverage-text
    

    Credits

    Acknowledgments

    Changelog

    See the changelog file

    Contributing

    Please see CONTRIBUTING for details.

    Support

    Bugs and feature request are tracked on GitHub

    Contributor Code of Conduct

    As contributors and maintainers of this project, we pledge to respect all people
    who contribute through reporting issues, posting feature requests, updating
    documentation, submitting pull requests or patches, and other activities.

    We are committed to making participation in this project a harassment-free
    experience for everyone, regardless of level of experience, gender, gender
    identity and expression, sexual orientation, disability, personal appearance,
    body size, race, age, or religion.

    Examples of unacceptable behavior by participants include the use of sexual
    language or imagery, derogatory comments or personal attacks, trolling, public
    or private harassment, insults, or other unprofessional conduct.

    Project maintainers have the right and responsibility to remove, edit, or reject
    comments, commits, code, wiki edits, issues, and other contributions that are
    not aligned to this Code of Conduct. Project maintainers who do not follow the
    Code of Conduct may be removed from the project team.

    Instances of abusive, harassing, or otherwise unacceptable behavior may be
    reported by opening an issue or contacting one or more of the project
    maintainers.

    This Code of Conduct is adapted from the Contributor
    Covenant
    , version 1.0.0, available at
    http://contributor-covenant.org/version/1/0/0/

    License

    Geotools is released under the MIT License. See the bundled
    LICENSE file for details.

    Bitdeli Badge

  • \League\Geotools\Tests Readme

    Geotools

    Geotools is a PHP geo-related library, built atop Geocoder and
    React libraries.

    Build Status
    Latest Version
    Total Downloads
    Quality Score
    SensioLabs Insight
    PHP7 Ready

    Features

    • Batch geocode & reverse geocoding request(s) in series / in parallel against one or a
      set of providers. »
    • Cache geocode & reverse geocoding result(s) with PSR-6 to improve performances. »
    • Compute geocode & reverse geocoding in the command-line interface (CLI) + dumpers and formatters. »
    • Accept almost all kind of WGS84
      geographic coordinates as coordinates.
      »
    • Support 23 different ellipsoids and it's easy to provide a new one if needed. »
    • Convert and format decimal degrees coordinates to decimal minutes or degrees minutes seconds coordinates.
      »
    • Convert decimal degrees coordinates in the
      Universal Transverse Mercator
      (UTM) projection. »
    • Compute the distance in meter (by default), km, mi or ft between two coordinates using flat,
      great circle, haversine or vincenty algorithms. »
    • Compute the initial and final bearing from the origin coordinate to the destination coordinate in degrees.
      »
    • Compute the initial and final cardinal point (direction) from the origin coordinate to the destination
      coordinate, read more in wikipedia. »
    • Compute the half-way point (coordinate) between the origin and the destination coordinates. »
    • Compute the destination point (coordinate) with given bearing in degrees and a distance in meters. »
    • Encode a coordinate to a geo hash string and decode it to a coordinate, read more in
      wikipedia and on geohash.org. »
    • Encode a coordinate via the 10:10 algorithm. »
    • Polygon class provides methods to check either a poing (coordinate) is in, or on the polygon's boundaries.
      »
    • A command-line interface (CLI) for Distance, Point, Geohash and Convert classes. »
    • Integration with Frameworks: Laravel 4, Silex ... »
    • ... more to come ...

    Installation

    Geotools can be found on Packagist.
    The recommended way to install Geotools is through composer.

    Run the following on the command line:

    php composer require league/geotools=@stable
    

    Protip: you should browse the
    league/geotools
    page to choose a stable version to use, avoid the @stable meta constraint.

    Important: you should use the 0.4 version if you use Geocoder 2.x or/and PHP 5.3.

    And install dependencies:

    $ curl -sS https://getcomposer.org/installer | php
    $ php composer.phar install
    

    Now you can add the autoloader, and you will have access to the library:

    <?php
    
    require 'vendor/autoload.php';
    

    Usage & API

    Coordinate & Ellipsoid

    The default geodetic datum is WGS84 and coordinates are in
    decimal degrees.

    Here are the available ellipsoids: AIRY, AUSTRALIAN_NATIONAL, BESSEL_1841, BESSEL_1841_NAMBIA,
    CLARKE_1866, CLARKE_1880, EVEREST, FISCHER_1960_MERCURY, FISCHER_1968, GRS_1967, GRS_1980,
    HELMERT_1906, HOUGH, INTERNATIONAL, KRASSOVSKY, MODIFIED_AIRY, MODIFIED_EVEREST,
    MODIFIED_FISCHER_1960, SOUTH_AMERICAN_1969, WGS60, WGS66, WGS72, and WGS84.

    If you need to use an other ellipsoid, just create an array like this:

    <?php
    
    $myEllipsoid = \League\Geotools\Coordinate\Ellipsoid::createFromArray([
        'name' => 'My Ellipsoid', // The name of the Ellipsoid
        'a'    => 123.0, // The semi-major axis (equatorial radius) in meters
        'invF' => 456.0 // The inverse flattening
    ]);
    

    Geotools is built atop Geocoder. It means it's possible to use the
    \Geocoder\Model\Address directly but it's also possible to use a string or a simple array with its
    latitude and longitude.

    It supports valid and acceptable geographic coordinates
    like:

    • 40:26:46N,079:56:55W
    • 40:26:46.302N 079:56:55.903W
    • 40°26′47″N 079°58′36″W
    • 40d 26′ 47″ N 079d 58′ 36″ W
    • 40.446195N 79.948862W
    • 40.446195, -79.948862
    • 40° 26.7717, -79° 56.93172

    Latitudes below -90.0 or above 90.0 degrees are capped through \League\Geotools\Coordinate\Coordinate::normalizeLatitude().
    Longitudes below -180.0 or above 180.0 degrees are wrapped through \League\Geotools\Coordinate\Coordinate::normalizeLongitude().

    <?php
    
    use League\Geotools\Coordinate\Coordinate;
    use League\Geotools\Coordinate\Ellipsoid;
    
    // from an \Geocoder\Model\Address instance within Airy ellipsoid
    $coordinate = new Coordinate($geocoderResult, Ellipsoid::createFromName(Ellipsoid::AIRY));
    // or in an array of latitude/longitude coordinate within GRS 1980 ellipsoid
    $coordinate = new Coordinate([48.8234055, 2.3072664], Ellipsoid::createFromName(Ellipsoid::GRS_1980));
    // or in latitude/longitude coordinate within WGS84 ellipsoid
    $coordinate = new Coordinate('48.8234055, 2.3072664');
    // or in degrees minutes seconds coordinate within WGS84 ellipsoid
    $coordinate = new Coordinate('48°49′24″N, 2°18′26″E');
    // or in decimal minutes coordinate within WGS84 ellipsoid
    $coordinate = new Coordinate('48 49.4N, 2 18.43333E');
    // the result will be:
    printf("Latitude: %F\n", $coordinate->getLatitude()); // 48.8234055
    printf("Longitude: %F\n", $coordinate->getLongitude()); // 2.3072664
    printf("Ellipsoid name: %s\n", $coordinate->getEllipsoid()->getName()); // WGS 84
    printf("Equatorial radius: %F\n", $coordinate->getEllipsoid()->getA()); // 6378136.0
    printf("Polar distance: %F\n", $coordinate->getEllipsoid()->getB()); // 6356751.317598
    printf("Inverse flattening: %F\n", $coordinate->getEllipsoid()->getInvF()); // 298.257224
    printf("Mean radius: %F\n", $coordinate->getEllipsoid()->getArithmeticMeanRadius()); // 6371007.772533
    // it's also possible to modify the coordinate without creating an other coodinate
    $coordinate->setFromString('40°26′47″N 079°58′36″W');
    printf("Latitude: %F\n", $coordinate->getLatitude()); // 40.446388888889
    printf("Longitude: %F\n", $coordinate->getLongitude()); // -79.976666666667
    

    Convert

    It provides methods (and aliases) to convert decimal degrees WGS84 coordinates to degrees minutes seconds
    or decimal minutes WGS84 coordinates. You can format the output string easily.

    You can also convert them in the Universal Transverse Mercator (UTM) projection (Southwest coast of Norway and the
    region of Svalbard are covered).

    <?php
    
    $geotools   = new \League\Geotools\Geotools();
    $coordinate = new \League\Geotools\Coordinate\Coordinate('40.446195, -79.948862');
    $converted  = $geotools->convert($coordinate);
    // convert to decimal degrees without and with format string
    printf("%s\n", $converted->toDecimalMinutes()); // 40 26.7717N, -79 56.93172W
    // convert to degrees minutes seconds without and with format string
    printf("%s\n", $converted->toDegreesMinutesSeconds('<p>%P%D:%M:%S, %p%d:%m:%s</p>')); // <p>40:26:46, -79:56:56</p>
    // convert in the UTM projection (standard format)
    printf("%s\n", $converted->toUniversalTransverseMercator()); // 17T 589138 4477813
    

    Here is the mapping:

    Decimal minutes Latitude Longitude
    Positive or negative sign %P %p
    Direction %L %l
    Degrees %D %d
    Decimal minutes %N %n
    Degrees minutes seconds Latitude Longitude
    Positive or negative sign %P %p
    Direction %L %l
    Degrees %D %d
    Minutes %M %m
    Seconds %S %s

    Batch

    It provides a very handy way to batch geocode and reverse geocoding requests in serie or in parallel against
    a set of providers.
    Thanks to Geocoder and React libraries.

    It's possible to batch one request (a string) or a set of request (an array) against one provider or
    set of providers.

    You can use a provided cache engine or use your own by setting a cache object which should implement
    League\Geotools\Cache\CacheInterface and extend League\Geotools\Cache\AbstractCache if needed.

    At the moment Geotools supports any PSR-6 cache.

    NB: Before you implement caching in your app please be sure that doing so does not violate the Terms of Service
    for your(s) geocoding provider(s).

    <?php
    
    $geocoder = new \Geocoder\ProviderAggregator(); // or \Geocoder\TimedGeocoder
    $httpClient  = HttpClientDiscovery::find();
    
    $geocoder->registerProviders([
        new \Geocoder\Provider\GoogleMaps\GoogleMaps($httpClient),
        new \Geocoder\Provider\OpenStreetMap\OpenStreetMap($httpClient),
        new \Geocoder\Provider\BingMaps\BingMaps($httpClient, '<FAKE_API_KEY>'), // throws InvalidCredentialsException
        new \Geocoder\Provider\Yandex\Yandex($httpClient),
        new \Geocoder\Provider\FreeGeoIp\FreeGeoIp($httpClient),
        new \Geocoder\Provider\Geoip\Geoip(),
    ]);
    
    try {
        $geotools = new \League\Geotools\Geotools();
        $cache    = new \Cache\Adapter\PHPArray\ArrayCachePool();
      
        $results  = $geotools->batch($geocoder)->setCache($cache)->geocode([
            'Paris, France',
            'Copenhagen, Denmark',
            '74.200.247.59',
            '::ffff:66.147.244.214'
        ])->parallel();
    } catch (\Exception $e) {
        die($e->getMessage());
    }
    
    $dumper = new \Geocoder\Dumper\WktDumper();
    foreach ($results as $result) {
        // if a provider throws an exception (UnsupportedException, InvalidCredentialsException ...)
        // an custom /Geocoder/Result/Geocoded instance is returned which embedded the name of the provider,
        // the query string and the exception string. It's possible to use dumpers
        // and/or formatters from the Geocoder library.
        printf("%s|%s|%s\n",
            $result->getProviderName(),
            $result->getQuery(),
            '' == $result->getExceptionMessage() ? $dumper->dump($result) : $result->getExceptionMessage()
        );
    }
    

    You should get 24 results (4 values to geocode against 6 providers) something like:

    google_maps|Paris, France|POINT(2.352222 48.856614)
    google_maps|Copenhagen, Denmark|POINT(12.568337 55.676097)
    google_maps|74.200.247.59|The GoogleMapsProvider does not support IP addresses.
    google_maps|::ffff:66.147.244.214|The GoogleMapsProvider does not support IP addresses.
    openstreetmaps|Paris, France|POINT(2.352133 48.856506)
    openstreetmaps|Copenhagen, Denmark|POINT(12.570072 55.686724)
    openstreetmaps|74.200.247.59|Could not execute query http://nominatim.openstreetmap.org/search?q=74.200.247.59&format=xml&addressdetails=1&limit=1
    openstreetmaps|::ffff:66.147.244.214|The OpenStreetMapProvider does not support IPv6 addresses.
    bing_maps|Paris, France|Could not execute query http://dev.virtualearth.net/REST/v1/Locations/?q=Paris%2C+France&key=<FAKE_API_KEY>
    bing_maps|Copenhagen, Denmark|Could not execute query http://dev.virtualearth.net/REST/v1/Locations/?q=Copenhagen%2C+Denmark&key=<FAKE_API_KEY>
    bing_maps|74.200.247.59|The BingMapsProvider does not support IP addresses.
    bing_maps|::ffff:66.147.244.214|The BingMapsProvider does not support IP addresses.
    yandex|Paris, France|POINT(2.341198 48.856929)
    yandex|Copenhagen, Denmark|POINT(12.567602 55.675682)
    yandex|74.200.247.59|The YandexProvider does not support IP addresses.
    yandex|::ffff:66.147.244.214|The YandexProvider does not support IP addresses.
    free_geo_ip|Paris, France|The FreeGeoIpProvider does not support Street addresses.
    free_geo_ip|Copenhagen, Denmark|The FreeGeoIpProvider does not support Street addresses.
    free_geo_ip|74.200.247.59|POINT(-122.415600 37.748400)
    free_geo_ip|::ffff:66.147.244.214|POINT(-111.613300 40.218100)
    geoip|Paris, France|The GeoipProvider does not support Street addresses.
    geoip|Copenhagen, Denmark|The GeoipProvider does not support Street addresses.
    geoip|74.200.247.59|POINT(-122.415604 37.748402)
    geoip|::ffff:66.147.244.214|The GeoipProvider does not support IPv6 addresses.
    

    Batch reverse geocoding is something like:

    <?php
    
    // ... $geocoder like the previous example ...
    // If you want to reverse one coordinate
    try {
        $results = $geotools->batch($geocoder)->reverse(
            new \League\Geotools\Coordinate\Coordinate([2.307266, 48.823405])
        )->parallel();
    } catch (\Exception $e) {
        die($e->getMessage());
    }
    // Or if you want to reverse geocoding 3 coordinates
    $coordinates = [
        new \League\Geotools\Coordinate\Coordinate([2.307266, 48.823405]),
        new \League\Geotools\Coordinate\Coordinate([12.568337, 55.676097]),
        new \League\Geotools\Coordinate\Coordinate('-74.005973 40.714353')),
    ];
    $results = $geotools->batch($geocoder)->reverse($coordinates)->parallel();
    // ...
    

    If you want to batch it in serie, replace the method parallel() by serie().

    To optimize batch requests you need to register providers according to their capabilities and what you're
    looking for (geocode street addresses, geocode IPv4, geocode IPv6 or reverse geocoding),
    please read more at the Geocoder library doc.

    Distance

    It provides methods to compute the distance in meter (by default), km, mi or ft between two coordinates
    using flat (most performant), great circle, haversine or vincenty (most accurate) algorithms.

    Those coordinates should be in the same ellipsoid.

    <?php
    
    $geotools = new \League\Geotools\Geotools();
    $coordA   = new \League\Geotools\Coordinate\Coordinate([48.8234055, 2.3072664]);
    $coordB   = new \League\Geotools\Coordinate\Coordinate([43.296482, 5.36978]);
    $distance = $geotools->distance()->setFrom($coordA)->setTo($coordB);
    
    printf("%s\n",$distance->flat()); // 659166.50038742 (meters)
    printf("%s\n",$distance->greatCircle()); // 659021.90812846
    printf("%s\n",$distance->in('km')->haversine()); // 659.02190812846
    printf("%s\n",$distance->in('mi')->vincenty()); // 409.05330679648
    printf("%s\n",$distance->in('ft')->flat()); // 2162619.7519272
    

    Point

    It provides methods to compute the initial and final bearing in degrees, the initial and final cardinal direction,
    the middle point and the destination point. The middle and the destination points returns a
    \League\Geotools\Coordinate\Coordinate object with the same ellipsoid.

    <?php
    
    $geotools = new \League\Geotools\Geotools();
    $coordA   = new \League\Geotools\Coordinate\Coordinate([48.8234055, 2.3072664]);
    $coordB   = new \League\Geotools\Coordinate\Coordinate([43.296482, 5.36978]);
    $vertex    =  $geotools->vertex()->setFrom($coordA)->setTo($coordB);
    
    printf("%d\n", $vertex->initialBearing()); // 157 (degrees)
    printf("%s\n", $vertex->initialCardinal()); // SSE (SouthSouthEast)
    printf("%d\n", $vertex->finalBearing()); // 160 (degrees)
    printf("%s\n", $vertex->finalCardinal()); // SSE (SouthSouthEast)
    
    $middlePoint = $vertex->middle(); // \League\Geotools\Coordinate\Coordinate
    printf("%s\n", $middlePoint->getLatitude()); // 46.070143125815
    printf("%s\n", $middlePoint->getLongitude()); // 3.9152401085931
    
    $destinationPoint = $geotools->vertex()->setFrom($coordA)->destination(180, 200000); // \League\Geotools\Coordinate\Coordinate
    printf("%s\n", $destinationPoint->getLatitude()); // 47.026774650075
    printf("%s\n", $destinationPoint->getLongitude()); // 2.3072664
    

    Geohash

    It provides methods to get the geo hash and its bounding box's coordinates (SouthWest & NorthEast)
    of a coordinate and the coordinate and its bounding box's coordinates (SouthWest & NorthEast) of a geo hash.

    <?php
    
    $geotools       = new \League\Geotools\Geotools();
    $coordToGeohash = new \League\Geotools\Coordinate\Coordinate('43.296482, 5.36978');
    
    // encoding
    $encoded = $geotools->geohash()->encode($coordToGeohash, 4); // 12 is the default length / precision
    // encoded
    printf("%s\n", $encoded->getGeohash()); // spey
    // encoded bounding box
    $boundingBox = $encoded->getBoundingBox(); // array of \League\Geotools\Coordinate\CoordinateInterface
    $southWest   = $boundingBox[0];
    $northEast   = $boundingBox[1];
    printf("http://www.openstreetmap.org/?minlon=%s&minlat=%s&maxlon=%s&maxlat=%s&box=yes\n",
        $southWest->getLongitude(), $southWest->getLatitude(),
        $northEast->getLongitude(), $northEast->getLatitude()
    ); // http://www.openstreetmap.org/?minlon=5.2734375&minlat=43.2421875&maxlon=5.625&maxlat=43.41796875&box=yes
    
    // decoding
    $decoded = $geotools->geohash()->decode('spey61y');
    // decoded coordinate
    printf("%s\n", $decoded->getCoordinate()->getLatitude()); // 43.296432495117
    printf("%s\n", $decoded->getCoordinate()->getLongitude()); // 5.3702545166016
    // decoded bounding box
    $boundingBox = $decoded->getBoundingBox(); //array of \League\Geotools\Coordinate\CoordinateInterface
    $southWest   = $boundingBox[0];
    $northEast   = $boundingBox[1];
    printf("http://www.openstreetmap.org/?minlon=%s&minlat=%s&maxlon=%s&maxlat=%s&box=yes\n",
        $southWest->getLongitude(), $southWest->getLatitude(),
        $northEast->getLongitude(), $northEast->getLatitude()
    ); // http://www.openstreetmap.org/?minlon=5.3695678710938&minlat=43.295745849609&maxlon=5.3709411621094&maxlat=43.297119140625&box=yes
    

    10:10

    Represent a location with 10m accuracy using a 10 character code that includes features to prevent errors in
    entering the code. Read more about the algorithm here.

    <?php
    
    $tenten = new \League\Geotools\Tests\Geohash\TenTen;
    $tenten->encode(new Coordinate([51.09559, 1.12207])); // MEQ N6G 7NY5
    

    Vertex

    Represents a segment with a direction.
    You can find if two vertexes are on the same line.

    <?php
    	$vertexA->setFrom(48.8234055);
    	$vertexA->setTo(2.3072664);
    
    	$vertexB->setFrom(48.8234055);
    	$vertexB->setTo(2.3072664);
    	$vertexA->isOnSameLine($vertexB);
    

    Polygon

    It helps you to know if a point (coordinate) is in a Polygon or on the Polygon's boundaries and if this in on
    a Polygon's vertex.

    First you need to create the polygon, you can provide:

    • an array of arrays
    • an array of Coordinate
    • a CoordinateCollection
    <?php
    
    $polygon = new \League\Geotools\Polygon\Polygon([
        [48.9675969, 1.7440796],
        [48.4711003, 2.5268555],
        [48.9279131, 3.1448364],
        [49.3895245, 2.6119995],
    ]);
    
    $polygon->setPrecision(5); // set the comparision precision
    $polygon->pointInPolygon(new \League\Geotools\Coordinate\Coordinate([49.1785607, 2.4444580])); // true
    $polygon->pointInPolygon(new \League\Geotools\Coordinate\Coordinate([49.1785607, 5])); // false
    $polygon->pointOnBoundary(new \League\Geotools\Coordinate\Coordinate([48.7193486, 2.13546755])); // true
    $polygon->pointOnBoundary(new \League\Geotools\Coordinate\Coordinate([47.1587188, 2.87841795])); // false
    $polygon->pointOnVertex(new \League\Geotools\Coordinate\Coordinate([48.4711003, 2.5268555])); // true
    $polygon->pointOnVertex(new \League\Geotools\Coordinate\Coordinate([49.1785607, 2.4444580])); // false
    $polygon->getBoundingBox(); // return the BoundingBox object
    

    CLI

    It provides command lines to compute methods provided by Distance, Point, Geohash and Convert classes.
    Thanks to the Symfony Console Component.

    $ php geotools list // list of available commands
    $ php geotools help distance:flat // get the help
    $ php geotools distance:flat "40° 26.7717, -79° 56.93172" "30°16′57″N 029°48′32″W" // 4690203.1048522
    $ php geotools distance:haversine "35,45" "45,35" --ft  // 4593030.9787593
    $ php geotools distance:vincenty "35,45" "45,35" --km  // 1398.4080717661
    $ php geotools d:v "35,45" "45,35" --km --ellipsoid=WGS60 // 1398.4145201642
    $ php geotools point:initial-cardinal "40:26:46.302N 079:56:55.903W" "43.296482, 5.36978" // NE (NordEast)
    $ php geotools point:final-cardinal "40:26:46.302N 079:56:55.903W" "43.296482, 5.36978" // ESE (EastSouthEast)
    $ php geotools point:destination "40° 26.7717, -79° 56.93172" 25 10000 // 40.527599285543, -79.898914904538
    $ php geotools p:d "40° 26.7717, -79° 56.93172" 25 10000 --ellipsoid=GRS_1980 // 40.527599272782, -79.898914912379
    $ php geotools geohash:encode "40° 26.7717, -79° 56.93172" --length=3 // dpp
    $ php geotools convert:dm "40.446195, -79.948862" --format="%P%D°%N %p%d°%n" // 40°26.7717 -79°56.93172
    $ php geotools convert:dms "40.446195, -79.948862" --format="%P%D:%M:%S, %p%d:%m:%s" // 40:26:46, -79:56:56
    $ php geotools convert:utm "60.3912628, 5.3220544" // 32V 297351 6700644
    $ php geotools c:u "60.3912628, 5.3220544" --ellipsoid=AIRY // 32V 297371 6700131
    ...
    

    Compute street addresses, IPv4s or IPv6s geocoding and reverse geocoding right in your console.

    It's possible to define and precise your request through these options:

    • --provider: bing_maps, yahoo, maxmind... google_maps is the default one. See the full list
      here.
    • --raw: the result output in RAW format, shows Adapter, Provider and Arguments if any.
    • --json: the result output in JSON string format.
    • --args: this option accepts multiple values (e.g. --args="API_KEY" --args="LOCALE") if your provider needs or
      can have arguments.
    • --dumper: this option is available for geocoding, gpx, geojson, kml, wkb and wkt by default.
      Read more here.
    • --format: this option is available for reverse geocoding, see the mapping
      here.
    $ php geotools help geocoder:geocode // get the help
    $ php geotools geocoder:geocode "Copenhagen, Denmark" // 55.6760968, 12.5683371
    $ php geotools geocoder:geocode "74.200.247.59" --provider="free_geo_ip" // 37.7484, -122.4156
    $ php geotools geocoder:geocode Paris --args="fr_FR" --args="France" --args="true" // 48.856614, 2.3522219
    $ php geotools geocoder:geocode Paris --dumper=wkt // POINT(2.352222 48.856614)
    ...
    $ php geotools geocoder:reverse "48.8631507, 2.388911" // Avenue Gambetta 10, 75020 Paris
    $ php geotools geocoder:reverse "48.8631507, 2.388911" --format="%L, %A1, %C" // Paris, Île-De-France, France
    $ php geotools geocoder:reverse "48.8631507, 2.388911" --format="%L, %A1, %C" --provider="openstreetmaps"
    // Paris, Île-De-France, France Métropolitaine
    ...
    $ php geotools geocoder:geocode "Tagensvej 47, Copenhagen" --raw --args=da_DK --args=Denmark
    

    The last command will show an output like this:

    HttpClient:    \Http\Client\Curl\Client
    Provider:      \Geocoder\Provider\GoogleMaps
    Cache:         \League\Geotools\Cache\Redis
    Arguments:     da_DK,Denmark
    ---
    Latitude:      55.699953
    Longitude:     12.552736
    Bounds
     - South: 55.699953
     - West:  12.552736
     - North: 55.699953
     - East:  12.552736
    Street Number: 47
    Street Name:   Tagensvej
    Zipcode:       2200
    City:          Copenhagen
    City District: København N
    County:        København
    County Code:   KØBENHAVN
    Region:        Capital Region Of Denmark
    Region Code:   CAPITAL REGION OF DENMARK
    Country:       Denmark
    Country Code:  DK
    Timezone:
    

    Integration with Frameworks

    Unit Tests

    To run unit tests, you'll need the cURL extension and a set of dependencies, you can install them using Composer:

    $ php composer.phar install --dev
    

    Once installed, just launch the following command:

    $ phpunit --coverage-text
    

    Credits

    Acknowledgments

    Changelog

    See the changelog file

    Contributing

    Please see CONTRIBUTING for details.

    Support

    Bugs and feature request are tracked on GitHub

    Contributor Code of Conduct

    As contributors and maintainers of this project, we pledge to respect all people
    who contribute through reporting issues, posting feature requests, updating
    documentation, submitting pull requests or patches, and other activities.

    We are committed to making participation in this project a harassment-free
    experience for everyone, regardless of level of experience, gender, gender
    identity and expression, sexual orientation, disability, personal appearance,
    body size, race, age, or religion.

    Examples of unacceptable behavior by participants include the use of sexual
    language or imagery, derogatory comments or personal attacks, trolling, public
    or private harassment, insults, or other unprofessional conduct.

    Project maintainers have the right and responsibility to remove, edit, or reject
    comments, commits, code, wiki edits, issues, and other contributions that are
    not aligned to this Code of Conduct. Project maintainers who do not follow the
    Code of Conduct may be removed from the project team.

    Instances of abusive, harassing, or otherwise unacceptable behavior may be
    reported by opening an issue or contacting one or more of the project
    maintainers.

    This Code of Conduct is adapted from the Contributor
    Covenant
    , version 1.0.0, available at
    http://contributor-covenant.org/version/1/0/0/

    License

    Geotools is released under the MIT License. See the bundled
    LICENSE file for details.

    Bitdeli Badge

  • \NXP Readme

    MathExecutor Tests

    A simple and extensible math expressions calculator

    Features:

    • Built in support for +, -, *, /, % and power (^) operators
    • Paratheses () and arrays [] are fully supported
    • Logical operators (==, !=, <, <, >=, <=, &&, ||)
    • Built in support for most PHP math functions
    • Support for BCMath Arbitrary Precision Math
    • Support for variable number of function parameters and optional function parameters
    • Conditional If logic
    • Support for user defined operators
    • Support for user defined functions
    • Support for math on user defined objects
    • Dynamic variable resolution (delayed computation)
    • Unlimited variable name lengths
    • String support, as function parameters or as evaluated as a number by PHP
    • Exceptions on divide by zero, or treat as zero
    • Unary Plus and Minus (e.g. +3 or -sin(12))
    • Pi ($pi) and Euler's number ($e) support to 11 decimal places
    • Easily extensible

    Install via Composer:

    composer require nxp/math-executor
    

    Sample usage:

    use NXP\MathExecutor;
    
    $executor = new MathExecutor();
    
    echo $executor->execute('1 + 2 * (2 - (4+10))^2 + sin(10)');
    

    Functions:

    Default functions:

    • abs
    • acos (arccos)
    • acosh
    • arcctg (arccot, arccotan)
    • arcsec
    • arccsc (arccosec)
    • array
    • asin (arcsin)
    • atan (atn, arctan, arctg)
    • atan2
    • atanh
    • avg
    • bindec
    • ceil
    • cos
    • cosec (csc)
    • cosh
    • ctg (cot, cotan, cotg, ctn)
    • decbin
    • dechex
    • decoct
    • deg2rad
    • exp
    • expm1
    • floor
    • fmod
    • hexdec
    • hypot
    • if
    • intdiv
    • log (ln)
    • log10 (lg)
    • log1p
    • max
    • min
    • octdec
    • pi
    • pow
    • rad2deg
    • round
    • sec
    • sin
    • sinh
    • sqrt
    • tan (tn, tg)
    • tanh

    Add custom function to executor:

    $executor->addFunction('concat', function($arg1, $arg2) {return $arg1 . $arg2;});
    

    Optional parameters:

    $executor->addFunction('round', function($num, int $precision = 0) {return round($num, $precision);});
    $executor->calculate('round(17.119)'); // 17
    $executor->calculate('round(17.119, 2)'); // 17.12
    

    Variable number of parameters:

    $executor->addFunction('avarage', function(...$args) {return array_sum($args) / count($args);});
    $executor->calculate('avarage(1,3)'); // 2
    $executor->calculate('avarage(1, 3, 4, 8)'); // 4
    

    Operators:

    Default operators: + - * / % ^

    Add custom operator to executor:

    use NXP\Classes\Operator;
    
    $executor->addOperator(new Operator(
        '%', // Operator sign
        false, // Is right associated operator
        180, // Operator priority
        function (&$stack)
        {
           $op2 = array_pop($stack);
           $op1 = array_pop($stack);
           $result = $op1->getValue() % $op2->getValue();
    
           return $result;
        }
    ));
    

    Logical operators:

    Logical operators (==, !=, <, <, >=, <=, &&, ||) are supported, but logically they can only return true (1) or false (0). In order to leverage them, use the built in if function:

    if($a > $b, $a - $b, $b - $a)
    

    You can think of the if function as prototyped like:

    function if($condition, $returnIfTrue, $returnIfFalse)
    

    Variables:

    Variables can be prefixed with the dollar sign ($) for PHP compatibility, but is not required.

    Default variables:

    $pi = 3.14159265359
    $e  = 2.71828182846
    

    You can add your own variables to executor:

    $executor->setVar('var1', 0.15)->setVar('var2', 0.22);
    
    echo $executor->execute("$var1 + var2");
    

    Arrays are also supported (as variables, as func params or can be returned in user defined funcs):

    $executor->setVar('monthly_salaries', [1800, 1900, 1200, 1600]);
    
    echo $executor->execute("avg(monthly_salaries) * min([1.1, 1.3])");
    

    By default, variables must be scalar values (int, float, bool or string) or array. If you would like to support another type, use setVarValidationHandler

    $executor->setVarValidationHandler(function (string $name, $variable) {
        // allow all scalars, array and null
        if (is_scalar($variable) || is_array($variable) || $variable === null) {
            return;
        }
        // Allow variables of type DateTime, but not others
        if (! $variable instanceof \DateTime) {
            throw new MathExecutorException("Invalid variable type");
        }
    });
    

    You can dynamically define variables at run time. If a variable has a high computation cost, but might not be used, then you can define an undefined variable handler. It will only get called when the variable is used, rather than having to always set it initially.

    $calculator = new MathExecutor();
    $calculator->setVarNotFoundHandler(
        function ($varName) {
            if ($varName == 'trans') {
                return transmogrify();
            }
            return null;
        }
    );
    

    Floating Point BCMath Support

    By default, MathExecutor uses PHP floating point math, but if you need a fixed precision, call useBCMath(). Precision defaults to 2 decimal points, or pass the required number.
    WARNING: Functions may return a PHP floating point number. By doing the basic math functions on the results, you will get back a fixed number of decimal points. Use a plus sign in front of any stand alone function to return the proper number of decimal places.

    Division By Zero Support:

    Division by zero throws a \NXP\Exception\DivisionByZeroException by default

    try {
        echo $executor->execute('1/0');
    } catch (DivisionByZeroException $e) {
        echo $e->getMessage();
    }
    

    Or call setDivisionByZeroIsZero

    echo $executor->setDivisionByZeroIsZero()->execute('1/0');
    

    If you want another behavior, you can override division operator:

    $executor->addOperator("/", false, 180, function($a, $b) {
        if ($b == 0) {
            return null;
        }
        return $a / $b;
    });
    echo $executor->execute('1/0');
    

    String Support:

    Expressions can contain double or single quoted strings that are evaluated the same way as PHP evaluates strings as numbers. You can also pass strings to functions.

    echo $executor->execute("1 + '2.5' * '.5' + myFunction('category')");
    

    To use reverse solidus character (\) in strings, or to use single quote character (') in a single quoted string, or to use double quote character (") in a double quoted string, you must prepend reverse solidus character (\).

    echo $executor->execute("countArticleSentences('My Best Article\'s Title')");
    

    Extending MathExecutor

    You can add operators, functions and variables with the public methods in MathExecutor, but if you need to do more serious modifications to base behaviors, the easiest way to extend MathExecutor is to redefine the following methods in your derived class:

    • defaultOperators
    • defaultFunctions
    • defaultVars

    This will allow you to remove functions and operators if needed, or implement different types more simply.

    Also note that you can replace an existing default operator by adding a new operator with the same regular expression string. For example if you just need to redefine TokenPlus, you can just add a new operator with the same regex string, in this case '\+'.

    Documentation

    Full class documentation via PHPFUI/InstaDoc

    Future Enhancements

    This package will continue to track currently supported versions of PHP.

  • \phpDocumentor\Reflection Readme

    License: MIT

    Coveralls Coverage
    Scrutinizer Code Coverage
    Scrutinizer Code Quality
    Packagist Version
    Packagist Version

    TypeResolver and FqsenResolver

    The specification on types in DocBlocks (PSR-5) describes various keywords and special constructs
    but also how to statically resolve the partial name of a Class into a Fully Qualified Class Name (FQCN).

    PSR-5 also introduces an additional way to describe deeper elements than Classes, Interfaces and Traits
    called the Fully Qualified Structural Element Name (FQSEN). Using this it is possible to refer to methods,
    properties and class constants but also functions and global constants.

    This package provides two Resolvers that are capable of

    1. Returning a series of Value Object for given expression while resolving any partial class names, and
    2. Returning an FQSEN object after resolving any partial Structural Element Names into Fully Qualified Structural
      Element names.

    Installing

    The easiest way to install this library is with Composer using the following command:

    $ composer require phpdocumentor/type-resolver
    

    Examples

    Ready to dive in and don't want to read through all that text below? Just consult the examples folder and check which type of action that your want to accomplish.

    On Types and Element Names

    This component can be used in one of two ways

    1. To resolve a Type or
    2. To resolve a Fully Qualified Structural Element Name

    The big difference between these two is in the number of things it can resolve.

    The TypeResolver can resolve:

    • a php primitive or pseudo-primitive such as a string or void (@var string or @return void).
    • a composite such as an array of string (@var string[]).
    • a compound such as a string or integer (@var string|integer).
    • an array expression (@var (string|TypeResolver)[])
    • an object or interface such as the TypeResolver class (@var TypeResolver
      or @var \phpDocumentor\Reflection\TypeResolver)

      please note that if you want to pass partial class names that additional steps are necessary, see the
      chapter Resolving partial classes and FQSENs for more information.

    Where the FqsenResolver can resolve:

    • Constant expressions (i.e. @see \MyNamespace\MY_CONSTANT)
    • Function expressions (i.e. @see \MyNamespace\myFunction())
    • Class expressions (i.e. @see \MyNamespace\MyClass)
    • Interface expressions (i.e. @see \MyNamespace\MyInterface)
    • Trait expressions (i.e. @see \MyNamespace\MyTrait)
    • Class constant expressions (i.e. @see \MyNamespace\MyClass::MY_CONSTANT)
    • Property expressions (i.e. @see \MyNamespace\MyClass::$myProperty)
    • Method expressions (i.e. @see \MyNamespace\MyClass::myMethod())

    Resolving a type

    In order to resolve a type you will have to instantiate the class \phpDocumentor\Reflection\TypeResolver and call its resolve method like this:

    $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
    $type = $typeResolver->resolve('string|integer');
    

    In this example you will receive a Value Object of class \phpDocumentor\Reflection\Types\Compound that has two
    elements, one of type \phpDocumentor\Reflection\Types\String_ and one of type
    \phpDocumentor\Reflection\Types\Integer.

    The real power of this resolver is in its capability to expand partial class names into fully qualified class names; but in order to do that we need an additional \phpDocumentor\Reflection\Types\Context class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.

    Resolving nullable types

    Php 7.1 introduced nullable types e.g. ?string. Type resolver will resolve the original type without the nullable notation ?
    just like it would do without the ?. After that the type is wrapped in a \phpDocumentor\Reflection\Types\Nullable object.
    The Nullable type has a method to fetch the actual type.

    Resolving an FQSEN

    A Fully Qualified Structural Element Name is a reference to another element in your code bases and can be resolved using the \phpDocumentor\Reflection\FqsenResolver class' resolve method, like this:

    $fqsenResolver = new \phpDocumentor\Reflection\FqsenResolver();
    $fqsen = $fqsenResolver->resolve('\phpDocumentor\Reflection\FqsenResolver::resolve()');
    

    In this example we resolve a Fully Qualified Structural Element Name (meaning that it includes the full namespace, class name and element name) and receive a Value Object of type \phpDocumentor\Reflection\Fqsen.

    The real power of this resolver is in its capability to expand partial element names into Fully Qualified Structural Element Names; but in order to do that we need an additional \phpDocumentor\Reflection\Types\Context class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.

    Resolving partial Classes and Structural Element Names

    Perhaps the best feature of this library is that it knows how to resolve partial class names into fully qualified class names.

    For example, you have this file:

    namespace My\Example;
    
    use phpDocumentor\Reflection\Types;
    
    class Classy
    {
        /**
         * @var Types\Context
         * @see Classy::otherFunction()
         */
        public function __construct($context) {}
        
        public function otherFunction(){}
    }
    

    Suppose that you would want to resolve (and expand) the type in the @var tag and the element name in the @see tag.

    For the resolvers to know how to expand partial names you have to provide a bit of Context for them by instantiating a new class named \phpDocumentor\Reflection\Types\Context with the name of the namespace and the aliases that are in play.

    Creating a Context

    You can do this by manually creating a Context like this:

    $context = new \phpDocumentor\Reflection\Types\Context(
        '\My\Example', 
        [ 'Types' => '\phpDocumentor\Reflection\Types']
    );
    

    Or by using the \phpDocumentor\Reflection\Types\ContextFactory to instantiate a new context based on a Reflector object or by providing the namespace that you'd like to extract and the source code of the file in which the given type expression occurs.

    $contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory();
    $context = $contextFactory->createFromReflector(new ReflectionMethod('\My\Example\Classy', '__construct'));
    

    or

    $contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory();
    $context = $contextFactory->createForNamespace('\My\Example', file_get_contents('My/Example/Classy.php'));
    

    Using the Context

    After you have obtained a Context it is just a matter of passing it along with the resolve method of either Resolver class as second argument and the Resolvers will take this into account when resolving partial names.

    To obtain the resolved class name for the @var tag in the example above you can do:

    $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
    $type = $typeResolver->resolve('Types\Context', $context);
    

    When you do this you will receive an object of class \phpDocumentor\Reflection\Types\Object_ for which you can call the getFqsen method to receive a Value Object that represents the complete FQSEN. So that would be phpDocumentor\Reflection\Types\Context.

    Why is the FQSEN wrapped in another object Object_?

    The resolve method of the TypeResolver only returns object with the interface Type and the FQSEN is a common type that does not represent a Type. Also: in some cases a type can represent an "Untyped Object", meaning that it is an object (signified by the object keyword) but does not refer to a specific element using an FQSEN.

    Another example is on how to resolve the FQSEN of a method as can be seen with the @see tag in the example above. To resolve that you can do the following:

    $fqsenResolver = new \phpDocumentor\Reflection\FqsenResolver();
    $type = $fqsenResolver->resolve('Classy::otherFunction()', $context);
    

    Because Classy is a Class in the current namespace its FQSEN will have the My\Example namespace and by calling the resolve method of the FQSEN Resolver you will receive an Fqsen object that refers to \My\Example\Classy::otherFunction().

  • \PHPFUI Readme

    PHPFUI Tests Latest Packagist release

    PHP Wrapper for the Foundation CSS Framework

    PHPFUI, PHP Foundation User Interface, is a modern PHP library that produces HTML formated for Foundation. It does everything you need for a fully functional Foundation page, with the power of an OO language. It currently uses Foundation 6.6.

    "I was surprised that people were prepared to write HTML. In my initial requirements for this thing, I had assumed, as an absolute pre-condition, that nobody would have to do HTML or deal with URLs. If you use the original World Wide Web program, you never see a URL or have to deal with HTML. You're presented with the raw information. You then input more information. So you are linking information to information--like using a word processor. That was a surprise to me--that people were prepared to painstakingly write HTML."

    Sir Tim Berners-Lee, inventor of the World Wide Web

    Using PHPFUI for view output will produce 100% valid HTML and insulate you from future changes to Foundation, your custom HMTL layouts, CSS and JS library changes. You write to an abstract concept (I want a checkbox here), and the library will output a checkbox formatted for Foundation. You can inherit from CheckBox and add your own take on a checkbox, and when the graphic designer decides they have the most awesome checkbox ever, you simply change your CheckBox class, and it is changed on every page system wide.

    Don't write HTML by hand!

    Usage

    namespace PHPFUI;
    $page = new Page();
    $form = new Form($page);
    $fieldset = new FieldSet('A basic input form');
    $time = new Input\Time($page, 'time', 'Enter A Time in 15 minute increments');
    $time->setRequired();
    $date = new Input\Date($page, 'date', 'Pick A Date');
    $fieldset->add(new MultiColumn($time, $date));
    $fieldset->add(new Input\TextArea('text', 'Enter some text'));
    $fieldset->add(new Submit());
    $form->add($fieldset);
    $page->add($form);
    $page->addStyleSheet('/css/styles.css');
    echo $page;

    Installation Instructions

    composer require phpfui/phpfui
    

    Then run update.php from the vendor/phpfui/phpfui directory and supply the path to your public directory / the directory for the various JS and CSS files PHPFUI uses. This will copy all required public files into your public directory. For example:

    php vendor/phpfui/phpfui/update.php public/PHPFUI
    

    The PHPFUI library defaults to your-public-directory/PHPFUI, it can be overridden, but it is suggested to use PHPFUI to keep everything in one place. update.php should be run when ever you update PHPFUI.

    Versioning

    Versioning will match the Foundation versions for Major semantic versions. PHPUI will always support the most recent version of Foundation possible for the Major version. PHPFUI Minor version will include breaking changes and may incorporate changes for the latest version of Foundation. The PHPFUI Patch version will include non breaking changes or additions. So PHPFUI Version 6.0.0 would be the first version of the library, 6.0.1 would be the first patch of PHPFUI. Both should work with any Foundation 6.x version. PHPFUI 6.1.0 will track PHP 7.4 - 8.1, 6.2.0 will track 8.0 - 8.2, but both will still track Foundation 6.x. PHPFUI 7.0.0 would track Foundation 7.x series on currently supported versions of PHP.

    Depreciation and Foundation changes

    Since major versions of Foundation have in the past depreciated and obsoleted things, PHPFUI will track the latest version of Foundation for class names and functionality. However, when Foundation makes a breaking change or removes something, PHPFUI will continue to support the old functionality as best as possible in the new Foundation framework. Depreciated classes will be put in the \PHPFUI\Vx namespace (where x would be the prior Major Foundation version containing that feature). So if something gets depreciated in a newer version of Foundation, you simply will need to change your code from \PHPFUI\Example to \PHPFUI\V6\Example. The depreciated namespace will only be supported for one Major version of PHPFUI, so it is recommended you migrate off of it in a timely manor.

    Full Class Documentation

    PHPFUI/InstaDoc

    Live Examples

    Via PHPFUI/Examples

    Unit Testing

    Full unit testing using phpfui/html-unit-tester

    License

    PHPFUI is distributed under the MIT License.

    PHP Versions

    This library only supports modern versions of PHP which still receive security updates. While we would love to support PHP from the late Ming Dynasty, the advantages of modern PHP versions far out weigh quaint notions of backward compatibility. Time to upgrade.

  • \PHPFUI\HTMLUnitTester Readme

    PHPFUI\HTMLUnitTester Tests Latest Packagist release

    PHPUnit Testing extensions for HMTL and CSS. PHPFUI\HTMLUnitTester allows you to unit test HTML and CSS for errors and warnings. Often simple errors in HTML or CSS create hard to debug issues where a simple check will reveal bad code.

    This package will check detect errors and warnings in HTML and CSS in stand alone strings, files, entire directories or urls.

    For the best performanance, a local install of https://github.com/validator/validator is recommended.

    Installation

    composer require phpfui/html-unit-tester
    

    Configuration

    It is recommended you run https://github.com/validator/validator locally. Install Java and download the .jar file. Run with the following command:

    java -Xss1024k -cp vnu.jar nu.validator.servlet.Main 8888
    

    If you are not using a local server, you will need to add the following lines to your phpunit.xml config in the phpunit element:

    <php>
      <env name="PHPFUI\HTMLUnitTester\Extensions_url" value="http://validator.w3.org/nu"/>
      <env name="PHPFUI\HTMLUnitTester\Extensions_delay" value="500000"/>
    </php>
    

    Usage

    Extend your unit tests from \PHPFUI\HTMLUnitTester\Extensions

    class UnitTest extends \PHPFUI\HTMLUnitTester\Extensions
      {
      public function testValidHtml()
        {
        $this->assertValidHtml('<h1>Header</h1>');
        $this->assertValidHtmlPage('<!DOCTYPE html><html><head><meta charset="utf-8"/><title>Title</title></head><body><div>This is a test</div></body></html>');
        }
      }
    

    You can use any of the following asserts:

    • assertNotWarningCss
    • assertNotWarningCssFile
    • assertNotWarningCssUrl
    • assertNotWarningFile
    • assertNotWarningHtml
    • assertNotWarningHtmlPage
    • assertNotWarningUrl
    • assertValidCss
    • assertValidCssFile
    • assertValidCssUrl
    • assertValidFile
    • assertValidHtml
    • assertValidHtmlPage
    • assertValidUrl

    Directory Testing

    Instead of file by file testing, use assertDirectory to test an entire directory. Any files added to the directory will be automatically tested.

    	$this->assertDirectory('ValidCSS', 'cssDirectory', 'Invalid CSS');
    	$this->assertDirectory('NotWarningCSS', 'cssDirectory', 'CSS has warnings');
    

    The error message will include the offending file name.

    Examples

    See examples

    Documentation

    Full documentation at PHPFUI\HTMLUnitTester

    License

    PHPFUI\HTMLUnitTester is distributed under the MIT License.

    PHP Versions

    This library only supports modern versions of PHP which still receive security updates. While we would love to support PHP from the late Ming Dynasty, the advantages of modern PHP versions far out weigh quaint notions of backward compatibility. Time to upgrade.

  • \PHPFUI\InstaDoc Readme

    PHPFUI\InstaDoc Library Tests Latest Packagist release

    A quick and easy way to add documentation to your PHP project

    We all document our code with PHP DocBlocks but we never seem to actually generate the documentation and add it to our project. Why? It simply takes too much time (over a minute), so we put it off till later, and later never comes.

    But with PHPFUI/InstaDoc, you can document your site in about a minute (OK, maybe 2). The steps involved:

    • Install PHPFUI/InstaDoc via Composer (30 seconds)
    • Run installation script (30 seconds)
    • Create document page (1 minute, 6 lines of code)

    Two minutes to usable documentation with the following features:

    PHPFUI/InstaDoc Features

    • Always up to date, even with code that is not yet checked in.
    • Send constructor information including parameters and default values to clipboard.
    • Child and Parent class hierarchy clearly displayed and accessable.
    • Quick access to highlighted PHP source with user selectable highlighting.
    • Quick access to the file's git history for the local repo.
    • Full support for @inheritDoc tag so child method docs are displayed correctly.
    • Documents all projects loaded via Composer automatically.
    • Tabbed documentation so you are not looking at irrelevant methods.
    • Alphabetized everything, no more searching unalphabetized pages!
    • Support for markdown and custom markdown pages.
    • Ability to generate static html files for high volume sites.
    • Add any local repo directories.
    • Remove any Composer project you don't care about.
    • 5+ line config compatible with all PHP frameworks, or standalone.
    • Uses Foundation CSS framework for a great experience on mobile.

    Install PHPFUI/InstaDoc (requires PHP >= 7.4)

    composer require phpfui/InstaDoc
    

    Run Installation Script

    Once installed, you need to run an installation script to copy static files to your public directory. From your project root, run the following:

    php vendor/phpfui/instadoc/install.php yourPublicDirectory/subDirectory
    

    Example: php vendor/phpfui/instadoc/install.php public/PHPFUI will add all needed files to public/PHPFUI, which will avoid any conflicts with your current files. You can specify any directory by using \PHPFUI\Page::setResourcePath, but PHPFUI is recomended to keep things simple.

    Create Document Page

    PHPFUI/InstaDoc does not reply on any framework and can run on a standalone page. It is recommended that you do not make your documentation public, as PHPFUI/InstaDoc will display PHP source files. How you restrict access to the page is up to you. The following does not restrict access and is simply an example:

    <?php
    include 'yourAutoLoader.php';
    
    // pass the directory containing your composer.json file
    $fileManager = new \PHPFUI\InstaDoc\FileManager('../');
    
    // add your App class tree in, pass true as the last parameter if this namespace is in your local git repo.
    $fileManager->addNamespace('App', '../App', true);
    
    // load your cached files
    $fileManager->load();
    
    // load child classes if you want to display them, if you don't do this step, docs will not show classes that extend the displayed class
    \PHPFUI\InstaDoc\ChildClasses::load();
    
    // get the controller
    $controller = new \PHPFUI\InstaDoc\Controller($fileManager);
    
    // display will return a fully formed page
    echo $controller->display();
    

    That is it. You are done!

    Adding New Classes

    PHPFUI/InstaDoc saves the classes to display in PHP serialized files. Delete those files (.serial extension) when you want to display new classes. PHPFUI/InstaDoc will regenerate automatically if the files are missing.

    Add Child Classes to the Docs

    \PHPFUI\InstaDoc\ChildClasses::load('../ChildClasses.serial');
    

    Add a Global Namespace Class

    The git repo path defaults to the composer directory, but you can change the path by calling:

    $fileManager->addGlobalNameSpaceClass(__DIR__ . '/global/FPDF.php');
    

    Removing a Namespace

    $fileManager->excludeNamespace('Carbon');
    

    Add git Repository Page

    The git repo path defaults to the composer directory, but you can change the path by calling:

    $controller->setGitRoot(getcwd() . '/../');
    

    Add Documents To Your Docs Home Page

    $controller->addHomePageMarkdown('../PHPFUI/InstaDoc/README.md');
    

    Set Your Home Page

    You may want users to get back into your system easily. Clicking on the top left menu bar will take them here:

    $controller->setHomeUrl('/');
    

    Breakup Your Documentation Into Sections

    If you have a lot of source code, you might want to break it into sections, so you will need a separate file to store the index in per section:

    $fileManager->setBaseFile('SubProject');
    

    Generate Static Files

    Just the doc and file pages, no git!

    $controller->generate('static/file/path', [\PHPFUI\InstaDoc\Controller::DOC_PAGE, \PHPFUI\InstaDoc\Controller::FILE_PAGE, ]));
    

    Examples and Full Class Documentation

    PHPFUI/InstaDoc

  • \PHPFUI\MySQLSlowQuery Readme

    PHPFUI\MySQLSlowLog\Parser Tests Latest Packagist release

    PHP Parser for MySQL Slow Query Logs featuring sortable results

    Requirements

    • PHP 7.1 or higher
    • MySQL 5.7 or higher

    Usage

    $parser = new \PHPFUI\MySQLSlowQuery\Parser($logFilePath);
    
    // Return the sessions in the file as array
    $sessions = $parser->getSessions();
    
    // Return all entries in file as array, or pass session number (0 based)
    $entries = $parser->getEntries();
    
    if (count($entries))
      {
      // Get the worst offender
      $entry = $parser->sortEntries()->getEntries()[0];
      echo 'Query ' . implode(' ', $entry->Query) . " took {$entry->Query_time} seconds at {$entry->Time}\n";
    
      // Get the most rows examined
      $entry = $parser->sortEntries('Rows_examined', 'desc')->getEntries()[0];
      echo 'Query ' . implode(' ', $entry->Query) . " looked at {$entry->Rows_examined} rows\n";
      }
    

    Entries

    \PHPFUI\MySQLSlowQuery\Entry provides details on each query.
    Supported fields:

    • Time
    • User
    • Host
    • Id
    • Query_time
    • Lock_time
    • Rows_sent
    • Rows_examined
    • Query (array)
    • Session (zero based)

    Sessions

    \PHPFUI\MySQLSlowQuery\Session contains MySQL server information and are created on server restarts and log flushes. Pass the zero based session number to getEntries for only that Session's entries.
    Supported fields:

    • Server
    • Port
    • Transport

    Sort Entries

    By default, entries are returned in log order, but call sortEntries on the Parser to sort by any valid field (parameter 1). Sort defaults to 'desc', anything else will sort ascending.

    Full Class Documentation

    Via PHPFUI/InstaDoc

    License

    Distributed under the MIT License.

  • \PHPFUI\PHPUnitSyntaxCoverage Readme

    PHPUnitSyntaxCoverage Tests Latest Packagist release

    PHPUnit Extension for complete PHP Syntax Code Coverage

    This package will checks for easy to miss syntax errors in all your PHP code. It will also check all your classes to see if they are loadable, but without actually instantiating the class.

    Often we accidently check in code with easily detectable syntax errors, but unless the file or class is actually loaded by PHP, we might not see the error. Often the file or class is syntaxically correct, but a method signature may not match an updated class or vendor library. Normally this would only be detectable at run time, but with PHPUnitSyntaxCoverage, you can make sure all files and classes are checked.

    PHPUnitSyntaxCoverage uses PhpParser to check for basic syntax errors. It then uses ReflectionClass to load any classes that are found in the source without instantiating them. This will find additional errors (such as missing or changed base classes from a package update).

    Requirements

    • Modern versions of PHP and PHPUnit
    • Correctly configured autoloading

    Installation

    composer require phpfui/phpunit-syntax-coverage
    

    Usage

    Extend your unit tests from \PHPFUI\PHPUnitSyntaxCoverage\Extensions

    class UnitTest extends \PHPFUI\PHPUnitSyntaxCoverage\Extensions
    	{
    	public function testProjectSyntax()
    		{
    		$this->addSkipDirectory(__DIR__ . '/../App/Examples');
    		$this->assertValidPHPDirectory(__DIR__ . '/../App', 'App directory has an error');
    		$this->assertValidPHPFile(__FILE__, 'Unit Test file not valid');
    		$this->assertValidPHP('<?php echo "hi";');
    		}
    	}
    

    You can use any of the following asserts:

    • assertValidPHP(string $code, string $message = '')
    • assertValidPHPDirectory(string $directory, string $message = '', bool $recurseSubdirectories = true, array $extensions = ['.php'])
    • assertValidPHPFile(string $fileName, string $message = '')

    Directory Testing

    Instead of file by file testing, use assertValidPHPDirectory to test an entire directory. Any files added to the directory will be automatically tested.

    	$this->assertValidPHPDirectory(__DIR__ . '/../App', 'App directory error');
    

    The error message will include the offending file name and line number.

    Use addSkipDirectory to add simple case insensitive file matching to skip specific directories / files.

    Autoloading

    You must make sure autoloading is correctly configured for all classes. This means you can't pass references to classes that will not resolve correctly in your source. Use addSkipDirectory if you have test code that may not validate correctly.

    Namespace Testing

    The assertValidPHPFile and assertValidPHPDirectory asserts will test for the proper namespace in the file path (for PSR-0 autoloading and fully pathed PSR-4 autoloading), but you can turn off namespace testing with skipNamespaceTesting or exclude a specific namespace tests with addSkipNamespace.

    PHP Version

    While this library only supports currently supported versions of PHP, you can create a project and point it to PHP 5.2 or higher. The default is to prefer PHP 7 code, but to prefer or only parse PHP 5, configure phpunit.xml(.dist) with

    	<php>
    		<env name="PHPFUI\PHPUnitSyntaxCoverage\Extensions_parser_type" value="X"/>
    	</php>
    

    Where X is one of the following numbers:

    1. Prefer PHP 7
    2. Prefer PHP 5
    3. Only PHP 7
    4. Only PHP 5

    Examples

    See examples

    Full Class Documentation

    PHPFUI/InstaDoc

    License

    PHPFUI is distributed under the MIT License.

  • \PHPMailer\PHPMailer Readme

    SWUbanner

    PHPMailer

    PHPMailer – A full-featured email creation and transfer class for PHP

    Test status
    codecov.io
    Latest Stable Version
    Total Downloads
    License
    API Docs
    OpenSSF Scorecard

    Features

    • Probably the world's most popular code for sending email from PHP!
    • Used by many open-source projects: WordPress, Drupal, 1CRM, SugarCRM, Yii, Joomla! and many more
    • Integrated SMTP support – send without a local mail server
    • Send emails with multiple To, CC, BCC and Reply-to addresses
    • Multipart/alternative emails for mail clients that do not read HTML email
    • Add attachments, including inline
    • Support for UTF-8 content and 8bit, base64, binary, and quoted-printable encodings
    • SMTP authentication with LOGIN, PLAIN, CRAM-MD5, and XOAUTH2 mechanisms over SMTPS and SMTP+STARTTLS transports
    • Validates email addresses automatically
    • Protects against header injection attacks
    • Error messages in over 50 languages!
    • DKIM and S/MIME signing support
    • Compatible with PHP 5.5 and later, including PHP 8.1
    • Namespaced to prevent name clashes
    • Much more!

    Why you might need it

    Many PHP developers need to send email from their code. The only PHP function that supports this directly is mail(). However, it does not provide any assistance for making use of popular features such as encryption, authentication, HTML messages, and attachments.

    Formatting email correctly is surprisingly difficult. There are myriad overlapping (and conflicting) standards, requiring tight adherence to horribly complicated formatting and encoding rules – the vast majority of code that you'll find online that uses the mail() function directly is just plain wrong, if not unsafe!

    The PHP mail() function usually sends via a local mail server, typically fronted by a sendmail binary on Linux, BSD, and macOS platforms, however, Windows usually doesn't include a local mail server; PHPMailer's integrated SMTP client allows email sending on all platforms without needing a local mail server. Be aware though, that the mail() function should be avoided when possible; it's both faster and safer to use SMTP to localhost.

    Please don't be tempted to do it yourself – if you don't use PHPMailer, there are many other excellent libraries that
    you should look at before rolling your own. Try SwiftMailer
    , Laminas/Mail, ZetaComponents etc.

    License

    This software is distributed under the LGPL 2.1 license, along with the GPL Cooperation Commitment. Please read LICENSE for information on the software availability and distribution.

    Installation & loading

    PHPMailer is available on Packagist (using semantic versioning), and installation via Composer is the recommended way to install PHPMailer. Just add this line to your composer.json file:

    "phpmailer/phpmailer": "^6.5"
    

    or run

    composer require phpmailer/phpmailer
    

    Note that the vendor folder and the vendor/autoload.php script are generated by Composer; they are not part of PHPMailer.

    If you want to use the Gmail XOAUTH2 authentication class, you will also need to add a dependency on the league/oauth2-client package in your composer.json.

    Alternatively, if you're not using Composer, you
    can download PHPMailer as a zip file, (note that docs and examples are not included in the zip file), then copy the contents of the PHPMailer folder into one of the include_path directories specified in your PHP configuration and load each class file manually:

    <?php
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\Exception;
    
    require 'path/to/PHPMailer/src/Exception.php';
    require 'path/to/PHPMailer/src/PHPMailer.php';
    require 'path/to/PHPMailer/src/SMTP.php';
    

    If you're not using the SMTP class explicitly (you're probably not), you don't need a use line for the SMTP class. Even if you're not using exceptions, you do still need to load the Exception class as it is used internally.

    Legacy versions

    PHPMailer 5.2 (which is compatible with PHP 5.0 — 7.0) is no longer supported, even for security updates. You will find the latest version of 5.2 in the 5.2-stable branch. If you're using PHP 5.5 or later (which you should be), switch to the 6.x releases.

    Upgrading from 5.2

    The biggest changes are that source files are now in the src/ folder, and PHPMailer now declares the namespace PHPMailer\PHPMailer. This has several important effects – read the upgrade guide for more details.

    Minimal installation

    While installing the entire package manually or with Composer is simple, convenient, and reliable, you may want to include only vital files in your project. At the very least you will need src/PHPMailer.php. If you're using SMTP, you'll need src/SMTP.php, and if you're using POP-before SMTP (very unlikely!), you'll need src/POP3.php. You can skip the language folder if you're not showing errors to users and can make do with English-only errors. If you're using XOAUTH2 you will need src/OAuth.php as well as the Composer dependencies for the services you wish to authenticate with. Really, it's much easier to use Composer!

    A Simple Example

    <?php
    //Import PHPMailer classes into the global namespace
    //These must be at the top of your script, not inside a function
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\SMTP;
    use PHPMailer\PHPMailer\Exception;
    
    //Load Composer's autoloader
    require 'vendor/autoload.php';
    
    //Create an instance; passing `true` enables exceptions
    $mail = new PHPMailer(true);
    
    try {
        //Server settings
        $mail->SMTPDebug = SMTP::DEBUG_SERVER;                      //Enable verbose debug output
        $mail->isSMTP();                                            //Send using SMTP
        $mail->Host       = 'smtp.example.com';                     //Set the SMTP server to send through
        $mail->SMTPAuth   = true;                                   //Enable SMTP authentication
        $mail->Username   = 'user@example.com';                     //SMTP username
        $mail->Password   = 'secret';                               //SMTP password
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;            //Enable implicit TLS encryption
        $mail->Port       = 465;                                    //TCP port to connect to; use 587 if you have set `SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS`
    
        //Recipients
        $mail->setFrom('from@example.com', 'Mailer');
        $mail->addAddress('joe@example.net', 'Joe User');     //Add a recipient
        $mail->addAddress('ellen@example.com');               //Name is optional
        $mail->addReplyTo('info@example.com', 'Information');
        $mail->addCC('cc@example.com');
        $mail->addBCC('bcc@example.com');
    
        //Attachments
        $mail->addAttachment('/var/tmp/file.tar.gz');         //Add attachments
        $mail->addAttachment('/tmp/image.jpg', 'new.jpg');    //Optional name
    
        //Content
        $mail->isHTML(true);                                  //Set email format to HTML
        $mail->Subject = 'Here is the subject';
        $mail->Body    = 'This is the HTML message body <b>in bold!</b>';
        $mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
    
        $mail->send();
        echo 'Message has been sent';
    } catch (Exception $e) {
        echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
    }
    

    You'll find plenty to play with in the examples folder, which covers many common scenarios including sending through gmail, building contact forms, sending to mailing lists, and more.

    If you are re-using the instance (e.g. when sending to a mailing list), you may need to clear the recipient list to avoid sending duplicate messages. See the mailing list example for further guidance.

    That's it. You should now be ready to use PHPMailer!

    Localization

    PHPMailer defaults to English, but in the language folder you'll find many translations for PHPMailer error messages that you may encounter. Their filenames contain ISO 639-1 language code for the translations, for example fr for French. To specify a language, you need to tell PHPMailer which one to use, like this:

    //To load the French version
    $mail->setLanguage('fr', '/optional/path/to/language/directory/');
    

    We welcome corrections and new languages – if you're looking for corrections, run the PHPMailerLangTest.php script in the tests folder and it will show any missing translations.

    Documentation

    Start reading at the GitHub wiki. If you're having trouble, head for the troubleshooting guide as it's frequently updated.

    Examples of how to use PHPMailer for common scenarios can be found in the examples folder. If you're looking for a good starting point, we recommend you start with the Gmail example.

    To reduce PHPMailer's deployed code footprint, examples are not included if you load PHPMailer via Composer or via GitHub's zip file download, so you'll need to either clone the git repository or use the above links to get to the examples directly.

    Complete generated API documentation is available online.

    You can generate complete API-level documentation by running phpdoc in the top-level folder, and documentation will appear in the docs folder, though you'll need to have PHPDocumentor installed. You may find the unit tests a good reference for how to do various operations such as encryption.

    If the documentation doesn't cover what you need, search the many questions on Stack Overflow, and before you ask a question about "SMTP Error: Could not connect to SMTP host.", read the troubleshooting guide.

    Tests

    PHPMailer tests use PHPUnit 9, with a polyfill to let 9-style tests run on older PHPUnit and PHP versions.

    Test status

    If this isn't passing, is there something you can do to help?

    Security

    Please disclose any vulnerabilities found responsibly – report security issues to the maintainers privately.

    See SECURITY and PHPMailer's security advisories on GitHub.

    Contributing

    Please submit bug reports, suggestions and pull requests to the GitHub issue tracker.

    We're particularly interested in fixing edge-cases, expanding test coverage and updating translations.

    If you found a mistake in the docs, or want to add something, go ahead and amend the wiki – anyone can edit it.

    If you have git clones from prior to the move to the PHPMailer GitHub organisation, you'll need to update any remote URLs referencing the old GitHub location with a command like this from within your clone:

    git remote set-url upstream https://github.com/PHPMailer/PHPMailer.git
    

    Please don't use the SourceForge or Google Code projects any more; they are obsolete and no longer maintained.

    Sponsorship

    Development time and resources for PHPMailer are provided by Smartmessages.net, the world's only privacy-first email marketing system.

    Smartmessages.net privacy-first email marketing logo

    Donations are very welcome, whether in beer 🍺, T-shirts 👕, or cold, hard cash 💰. Sponsorship through GitHub is a simple and convenient way to say "thank you" to PHPMailer's maintainers and contributors – just click the "Sponsor" button on the project page. If your company uses PHPMailer, consider taking part in Tidelift's enterprise support programme.

    PHPMailer For Enterprise

    Available as part of the Tidelift Subscription.

    The maintainers of PHPMailer and thousands of other packages are working with Tidelift to deliver commercial
    support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and
    improve code health, while paying the maintainers of the exact packages you
    use. Learn more.

    Changelog

    See changelog.

    History

    • PHPMailer was originally written in 2001 by Brent R. Matzelle as a SourceForge project.
    • Marcus Bointon (coolbru on SF) and Andy Prevost (codeworxtech) took over the project in 2004.
    • Became an Apache incubator project on Google Code in 2010, managed by Jim Jagielski.
    • Marcus created his fork on GitHub in 2008.
    • Jim and Marcus decide to join forces and use GitHub as the canonical and official repo for PHPMailer in 2013.
    • PHPMailer moves to the PHPMailer organisation on GitHub in 2013.

    What's changed since moving from SourceForge?

    • Official successor to the SourceForge and Google Code projects.
    • Test suite.
    • Continuous integration with Github Actions.
    • Composer support.
    • Public development.
    • Additional languages and language strings.
    • CRAM-MD5 authentication support.
    • Preserves full repo history of authors, commits and branches from the original SourceForge project.
  • \PhpParser Readme

    PHP Parser

    Coverage Status

    This is a PHP 5.2 to PHP 8.2 parser written in PHP. Its purpose is to simplify static code analysis and
    manipulation.

    Documentation for version 4.x (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.2).

    Documentation for version 3.x (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).

    Features

    The main features provided by this library are:

    • Parsing PHP 5, PHP 7, and PHP 8 code into an abstract syntax tree (AST).
      • Invalid code can be parsed into a partial AST.
      • The AST contains accurate location information.
    • Dumping the AST in human-readable form.
    • Converting an AST back to PHP code.
      • Experimental: Formatting can be preserved for partially changed ASTs.
    • Infrastructure to traverse and modify ASTs.
    • Resolution of namespaced names.
    • Evaluation of constant expressions.
    • Builders to simplify AST construction for code generation.
    • Converting an AST into JSON and back.

    Quick Start

    Install the library using composer:

    php composer.phar require nikic/php-parser
    

    Parse some PHP code into an AST and dump the result in human-readable form:

    <?php
    use PhpParser\Error;
    use PhpParser\NodeDumper;
    use PhpParser\ParserFactory;
    
    $code = <<<'CODE'
    <?php
    
    function test($foo)
    {
        var_dump($foo);
    }
    CODE;
    
    $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
    try {
        $ast = $parser->parse($code);
    } catch (Error $error) {
        echo "Parse error: {$error->getMessage()}\n";
        return;
    }
    
    $dumper = new NodeDumper;
    echo $dumper->dump($ast) . "\n";
    

    This dumps an AST looking something like this:

    array(
        0: Stmt_Function(
            byRef: false
            name: Identifier(
                name: test
            )
            params: array(
                0: Param(
                    type: null
                    byRef: false
                    variadic: false
                    var: Expr_Variable(
                        name: foo
                    )
                    default: null
                )
            )
            returnType: null
            stmts: array(
                0: Stmt_Expression(
                    expr: Expr_FuncCall(
                        name: Name(
                            parts: array(
                                0: var_dump
                            )
                        )
                        args: array(
                            0: Arg(
                                value: Expr_Variable(
                                    name: foo
                                )
                                byRef: false
                                unpack: false
                            )
                        )
                    )
                )
            )
        )
    )
    

    Let's traverse the AST and perform some kind of modification. For example, drop all function bodies:

    use PhpParser\Node;
    use PhpParser\Node\Stmt\Function_;
    use PhpParser\NodeTraverser;
    use PhpParser\NodeVisitorAbstract;
    
    $traverser = new NodeTraverser();
    $traverser->addVisitor(new class extends NodeVisitorAbstract {
        public function enterNode(Node $node) {
            if ($node instanceof Function_) {
                // Clean out the function body
                $node->stmts = [];
            }
        }
    });
    
    $ast = $traverser->traverse($ast);
    echo $dumper->dump($ast) . "\n";
    

    This gives us an AST where the Function_::$stmts are empty:

    array(
        0: Stmt_Function(
            byRef: false
            name: Identifier(
                name: test
            )
            params: array(
                0: Param(
                    type: null
                    byRef: false
                    variadic: false
                    var: Expr_Variable(
                        name: foo
                    )
                    default: null
                )
            )
            returnType: null
            stmts: array(
            )
        )
    )
    

    Finally, we can convert the new AST back to PHP code:

    use PhpParser\PrettyPrinter;
    
    $prettyPrinter = new PrettyPrinter\Standard;
    echo $prettyPrinter->prettyPrintFile($ast);
    

    This gives us our original code, minus the var_dump() call inside the function:

    <?php
    
    function test($foo)
    {
    }
    

    For a more comprehensive introduction, see the documentation.

    Documentation

    1. Introduction
    2. Usage of basic components

    Component documentation:

    • Walking the AST
      • Node visitors
      • Modifying the AST from a visitor
      • Short-circuiting traversals
      • Interleaved visitors
      • Simple node finding API
      • Parent and sibling references
    • Name resolution
      • Name resolver options
      • Name resolution context
    • Pretty printing
      • Converting AST back to PHP code
      • Customizing formatting
      • Formatting-preserving code transformations
    • AST builders
      • Fluent builders for AST nodes
    • Lexer
      • Lexer options
      • Token and file positions for nodes
      • Custom attributes
    • Error handling
      • Column information for errors
      • Error recovery (parsing of syntactically incorrect code)
    • Constant expression evaluation
      • Evaluating constant/property/etc initializers
      • Handling errors and unsupported expressions
    • JSON representation
      • JSON encoding and decoding of ASTs
    • Performance
      • Disabling Xdebug
      • Reusing objects
      • Garbage collection impact
    • Frequently asked questions
      • Parent and sibling references
  • \PhpParser\grammar Readme

    What do all those files mean?

    • php5.y: PHP 5 grammar written in a pseudo language
    • php7.y: PHP 7 grammar written in a pseudo language
    • tokens.y: Tokens definition shared between PHP 5 and PHP 7 grammars
    • parser.template: A kmyacc parser prototype file for PHP
    • tokens.template: A kmyacc prototype file for the Tokens class
    • rebuildParsers.php: Preprocesses the grammar and builds the parser using kmyacc

    .phpy pseudo language

    The .y file is a normal grammar in kmyacc (yacc) style, with some transformations
    applied to it:

    • Nodes are created using the syntax Name[..., ...]. This is transformed into
      new Name(..., ..., attributes())
    • Some function-like constructs are resolved (see rebuildParsers.php for a list)

    Building the parser

    Run php grammar/rebuildParsers.php to rebuild the parsers. Additional options:

    • The KMYACC environment variable can be used to specify an alternative kmyacc binary.
      By default the phpyacc dev dependency will be used. To use the original kmyacc, you
      need to compile moriyoshi's fork.
    • The --debug option enables emission of debug symbols and creates the y.output file.
    • The --keep-tmp-grammar option preserves the preprocessed grammar file.
  • \Symfony\Component\Console Readme

    Console Component

    The Console component eases the creation of beautiful and testable command line
    interfaces.

    Resources

    Credits

    Resources/bin/hiddeninput.exe is a third party binary provided within this
    component. Find sources and license at https://github.com/Seldaek/hidden-input.

  • \Symfony\Component\CssSelector Readme

    CssSelector Component

    The CssSelector component converts CSS selectors to XPath expressions.

    Resources

    Credits

    This component is a port of the Python cssselect library
    v0.7.1,
    which is distributed under the BSD license.

  • \Symfony\Component\DependencyInjection Readme

    DependencyInjection Component

    The DependencyInjection component allows you to standardize and centralize the
    way objects are constructed in your application.

    Resources

  • \Symfony\Component\Process Readme

    Process Component

    The Process component executes commands in sub-processes.

    Sponsor

    The Process component for Symfony 6.1 is backed by SensioLabs.

    As the creator of Symfony, SensioLabs supports companies using Symfony, with an
    offering encompassing consultancy, expertise, services, training, and technical
    assistance to ensure the success of web application development projects.

    Help Symfony by sponsoring its development!

    Resources

  • \Symfony\Component\PropertyAccess Readme

    PropertyAccess Component

    The PropertyAccess component provides functions to read and write from/to an
    object or array using a simple string notation.

    Resources

  • \Symfony\Component\PropertyInfo Readme

    PropertyInfo Component

    The PropertyInfo component extracts information about PHP class' properties
    using metadata of popular sources.

    Resources

  • \Symfony\Component\Serializer Readme

    Serializer Component

    The Serializer component handles serializing and deserializing data structures,
    including object graphs, into array structures or other formats like XML and
    JSON.

    Resources

  • \Symfony\Component\String Readme

    String Component

    The String component provides an object-oriented API to strings and deals
    with bytes, UTF-8 code points and grapheme clusters in a unified way.

    Resources

  • \Symfony\Contracts\Service Readme

    Symfony Service Contracts

    A set of abstractions extracted out of the Symfony components.

    Can be used to build on semantics that the Symfony components proved useful - and
    that already have battle tested implementations.

    See https://github.com/symfony/contracts/blob/main/README.md for more information.

  • \voku\helper Readme

    Build Status
    Coverage Status
    Codacy Badge
    Latest Stable Version
    Total Downloads
    License
    Donate to this project using Paypal
    Donate to this project using Patreon

    :scroll: Simple Html Dom Parser for PHP

    A HTML DOM parser written in PHP - let you manipulate HTML in a very easy way!
    This is a fork of PHP Simple HTML DOM Parser project but instead of string manipulation we use DOMDocument and modern php classes like "Symfony CssSelector".

    • PHP 7.0+ & 8.0 Support
    • PHP-FIG Standard
    • Composer & PSR-4 support
    • PHPUnit testing via Travis CI
    • PHP-Quality testing via SensioLabsInsight
    • UTF-8 Support (more support via "voku/portable-utf8")
    • Invalid HTML Support (partly ...)
    • Find tags on an HTML page with selectors just like jQuery
    • Extract contents from HTML in a single line

    Install via "composer require"

    composer require voku/simple_html_dom
    composer require voku/portable-utf8 # if you need e.g. UTF-8 fixed output
    

    Quick Start

    use voku\helper\HtmlDomParser;
    
    require_once 'composer/autoload.php';
    
    ...
    $dom = HtmlDomParser::str_get_html($str);
    // or 
    $dom = HtmlDomParser::file_get_html($file);
    
    $element = $dom->findOne('#css-selector'); // "$element" === instance of "SimpleHtmlDomInterface"
    
    $elements = $dom->findMulti('.css-selector'); // "$elements" === instance of SimpleHtmlDomNodeInterface<int, SimpleHtmlDomInterface>
    
    $elementOrFalse = $dom->findOneOrFalse('#css-selector'); // "$elementOrFalse" === instance of "SimpleHtmlDomInterface" or false
    
    $elementsOrFalse = $dom->findMultiOrFalse('.css-selector'); // "$elementsOrFalse" === instance of SimpleHtmlDomNodeInterface<int, SimpleHtmlDomInterface> or false
    ...
    
    

    Examples

    github.com/voku/simple_html_dom/tree/master/example

    API

    github.com/voku/simple_html_dom/tree/master/README_API.md

    Support

    For support and donations please visit Github | Issues | PayPal | Patreon.

    For status updates and release announcements please visit Releases | Twitter | Patreon.

    For professional support please contact me.

    Thanks

    • Thanks to GitHub (Microsoft) for hosting the code and a good infrastructure including Issues-Managment, etc.
    • Thanks to IntelliJ as they make the best IDEs for PHP and they gave me an open source license for PhpStorm!
    • Thanks to Travis CI for being the most awesome, easiest continous integration tool out there!
    • Thanks to StyleCI for the simple but powerfull code style check.
    • Thanks to PHPStan && Psalm for relly great Static analysis tools and for discover bugs in the code!

    License

    FOSSA Status

  • \Webmozart\Assert Readme

    Webmozart Assert

    Latest Stable Version
    Total Downloads

    This library contains efficient assertions to test the input and output of
    your methods. With these assertions, you can greatly reduce the amount of coding
    needed to write a safe implementation.

    All assertions in the Assert class throw an Webmozart\Assert\InvalidArgumentException if
    they fail.

    FAQ

    What's the difference to beberlei/assert?

    This library is heavily inspired by Benjamin Eberlei's wonderful assert package,
    but fixes a usability issue with error messages that can't be fixed there without
    breaking backwards compatibility.

    This package features usable error messages by default. However, you can also
    easily write custom error messages:

    Assert::string($path, 'The path is expected to be a string. Got: %s');
    

    In beberlei/assert, the ordering of the %s placeholders is different for
    every assertion. This package, on the contrary, provides consistent placeholder
    ordering for all assertions:

    • %s: The tested value as string, e.g. "/foo/bar".
    • %2$s, %3$s, ...: Additional assertion-specific values, e.g. the
      minimum/maximum length, allowed values, etc.

    Check the source code of the assertions to find out details about the additional
    available placeholders.

    Installation

    Use Composer to install the package:

    composer require webmozart/assert
    

    Example

    use Webmozart\Assert\Assert;
    
    class Employee
    {
        public function __construct($id)
        {
            Assert::integer($id, 'The employee ID must be an integer. Got: %s');
            Assert::greaterThan($id, 0, 'The employee ID must be a positive integer. Got: %s');
        }
    }
    

    If you create an employee with an invalid ID, an exception is thrown:

    new Employee('foobar');
    // => Webmozart\Assert\InvalidArgumentException:
    //    The employee ID must be an integer. Got: string
    
    new Employee(-10);
    // => Webmozart\Assert\InvalidArgumentException:
    //    The employee ID must be a positive integer. Got: -10
    

    Assertions

    The Assert class provides the following assertions:

    Type Assertions

    Method Description
    string($value, $message = '') Check that a value is a string
    stringNotEmpty($value, $message = '') Check that a value is a non-empty string
    integer($value, $message = '') Check that a value is an integer
    integerish($value, $message = '') Check that a value casts to an integer
    positiveInteger($value, $message = '') Check that a value is a positive (non-zero) integer
    float($value, $message = '') Check that a value is a float
    numeric($value, $message = '') Check that a value is numeric
    natural($value, $message= ''') Check that a value is a non-negative integer
    boolean($value, $message = '') Check that a value is a boolean
    scalar($value, $message = '') Check that a value is a scalar
    object($value, $message = '') Check that a value is an object
    resource($value, $type = null, $message = '') Check that a value is a resource
    isCallable($value, $message = '') Check that a value is a callable
    isArray($value, $message = '') Check that a value is an array
    isTraversable($value, $message = '') (deprecated) Check that a value is an array or a \Traversable
    isIterable($value, $message = '') Check that a value is an array or a \Traversable
    isCountable($value, $message = '') Check that a value is an array or a \Countable
    isInstanceOf($value, $class, $message = '') Check that a value is an instanceof a class
    isInstanceOfAny($value, array $classes, $message = '') Check that a value is an instanceof at least one class on the array of classes
    notInstanceOf($value, $class, $message = '') Check that a value is not an instanceof a class
    isAOf($value, $class, $message = '') Check that a value is of the class or has one of its parents
    isAnyOf($value, array $classes, $message = '') Check that a value is of at least one of the classes or has one of its parents
    isNotA($value, $class, $message = '') Check that a value is not of the class or has not one of its parents
    isArrayAccessible($value, $message = '') Check that a value can be accessed as an array
    uniqueValues($values, $message = '') Check that the given array contains unique values

    Comparison Assertions

    Method Description
    true($value, $message = '') Check that a value is true
    false($value, $message = '') Check that a value is false
    notFalse($value, $message = '') Check that a value is not false
    null($value, $message = '') Check that a value is null
    notNull($value, $message = '') Check that a value is not null
    isEmpty($value, $message = '') Check that a value is empty()
    notEmpty($value, $message = '') Check that a value is not empty()
    eq($value, $value2, $message = '') Check that a value equals another (==)
    notEq($value, $value2, $message = '') Check that a value does not equal another (!=)
    same($value, $value2, $message = '') Check that a value is identical to another (===)
    notSame($value, $value2, $message = '') Check that a value is not identical to another (!==)
    greaterThan($value, $value2, $message = '') Check that a value is greater than another
    greaterThanEq($value, $value2, $message = '') Check that a value is greater than or equal to another
    lessThan($value, $value2, $message = '') Check that a value is less than another
    lessThanEq($value, $value2, $message = '') Check that a value is less than or equal to another
    range($value, $min, $max, $message = '') Check that a value is within a range
    inArray($value, array $values, $message = '') Check that a value is one of a list of values
    oneOf($value, array $values, $message = '') Check that a value is one of a list of values (alias of inArray)

    String Assertions

    You should check that a value is a string with Assert::string() before making
    any of the following assertions.

    Method Description
    contains($value, $subString, $message = '') Check that a string contains a substring
    notContains($value, $subString, $message = '') Check that a string does not contain a substring
    startsWith($value, $prefix, $message = '') Check that a string has a prefix
    notStartsWith($value, $prefix, $message = '') Check that a string does not have a prefix
    startsWithLetter($value, $message = '') Check that a string starts with a letter
    endsWith($value, $suffix, $message = '') Check that a string has a suffix
    notEndsWith($value, $suffix, $message = '') Check that a string does not have a suffix
    regex($value, $pattern, $message = '') Check that a string matches a regular expression
    notRegex($value, $pattern, $message = '') Check that a string does not match a regular expression
    unicodeLetters($value, $message = '') Check that a string contains Unicode letters only
    alpha($value, $message = '') Check that a string contains letters only
    digits($value, $message = '') Check that a string contains digits only
    alnum($value, $message = '') Check that a string contains letters and digits only
    lower($value, $message = '') Check that a string contains lowercase characters only
    upper($value, $message = '') Check that a string contains uppercase characters only
    length($value, $length, $message = '') Check that a string has a certain number of characters
    minLength($value, $min, $message = '') Check that a string has at least a certain number of characters
    maxLength($value, $max, $message = '') Check that a string has at most a certain number of characters
    lengthBetween($value, $min, $max, $message = '') Check that a string has a length in the given range
    uuid($value, $message = '') Check that a string is a valid UUID
    ip($value, $message = '') Check that a string is a valid IP (either IPv4 or IPv6)
    ipv4($value, $message = '') Check that a string is a valid IPv4
    ipv6($value, $message = '') Check that a string is a valid IPv6
    email($value, $message = '') Check that a string is a valid e-mail address
    notWhitespaceOnly($value, $message = '') Check that a string contains at least one non-whitespace character

    File Assertions

    Method Description
    fileExists($value, $message = '') Check that a value is an existing path
    file($value, $message = '') Check that a value is an existing file
    directory($value, $message = '') Check that a value is an existing directory
    readable($value, $message = '') Check that a value is a readable path
    writable($value, $message = '') Check that a value is a writable path

    Object Assertions

    Method Description
    classExists($value, $message = '') Check that a value is an existing class name
    subclassOf($value, $class, $message = '') Check that a class is a subclass of another
    interfaceExists($value, $message = '') Check that a value is an existing interface name
    implementsInterface($value, $class, $message = '') Check that a class implements an interface
    propertyExists($value, $property, $message = '') Check that a property exists in a class/object
    propertyNotExists($value, $property, $message = '') Check that a property does not exist in a class/object
    methodExists($value, $method, $message = '') Check that a method exists in a class/object
    methodNotExists($value, $method, $message = '') Check that a method does not exist in a class/object

    Array Assertions

    Method Description
    keyExists($array, $key, $message = '') Check that a key exists in an array
    keyNotExists($array, $key, $message = '') Check that a key does not exist in an array
    validArrayKey($key, $message = '') Check that a value is a valid array key (int or string)
    count($array, $number, $message = '') Check that an array contains a specific number of elements
    minCount($array, $min, $message = '') Check that an array contains at least a certain number of elements
    maxCount($array, $max, $message = '') Check that an array contains at most a certain number of elements
    countBetween($array, $min, $max, $message = '') Check that an array has a count in the given range
    isList($array, $message = '') Check that an array is a non-associative list
    isNonEmptyList($array, $message = '') Check that an array is a non-associative list, and not empty
    isMap($array, $message = '') Check that an array is associative and has strings as keys
    isNonEmptyMap($array, $message = '') Check that an array is associative and has strings as keys, and is not empty

    Function Assertions

    Method Description
    throws($closure, $class, $message = '') Check that a function throws a certain exception. Subclasses of the exception class will be accepted.

    Collection Assertions

    All of the above assertions can be prefixed with all*() to test the contents
    of an array or a \Traversable:

    Assert::allIsInstanceOf($employees, 'Acme\Employee');
    

    Nullable Assertions

    All of the above assertions can be prefixed with nullOr*() to run the
    assertion only if it the value is not null:

    Assert::nullOrString($middleName, 'The middle name must be a string or null. Got: %s');
    

    Extending Assert

    The Assert class comes with a few methods, which can be overridden to change the class behaviour. You can also extend it to
    add your own assertions.

    Overriding methods

    Overriding the following methods in your assertion class allows you to change the behaviour of the assertions:

    • public static function __callStatic($name, $arguments)
      • This method is used to 'create' the nullOr and all versions of the assertions.
    • protected static function valueToString($value)
      • This method is used for error messages, to convert the value to a string value for displaying. You could use this for representing a value object with a __toString method for example.
    • protected static function typeToString($value)
      • This method is used for error messages, to convert the a value to a string representing its type.
    • protected static function strlen($value)
      • This method is used to calculate string length for relevant methods, using the mb_strlen if available and useful.
    • protected static function reportInvalidArgument($message)
      • This method is called when an assertion fails, with the specified error message. Here you can throw your own exception, or log something.

    Static analysis support

    Where applicable, assertion functions are annotated to support Psalm's
    Assertion syntax.
    A dedicated PHPStan Plugin is
    required for proper type support.

    Authors

    Contribute

    Contributions to the package are always welcome!

    License

    All contents of this package are licensed under the MIT license.

  • \ZBateson\MailMimeParser Readme

    zbateson/mail-mime-parser

    Testable and PSR-compliant mail mime parser alternative to PHP's imap* functions and Pear libraries for reading messages in Internet Message Format RFC 822 (and later revisions RFC 2822, RFC 5322).

    Build Status
    Code Coverage
    Scrutinizer Code Quality
    Total Downloads
    Latest Stable Version

    The goals of this project are to be:

    • Well written
    • Standards-compliant but forgiving
    • Tested where possible

    To include it for use in your project, install it via composer:

    composer require zbateson/mail-mime-parser
    

    Sponsors

    SecuMailer

    A huge thank you to all my sponsors. <3

    If this project's helped you, please consider sponsoring me.

    Removal Notice (since 2.0.0)

    getContentResourceHandle, getTextResourceHandle, and getHtmlResourceHandle have all been deprecated in 1.2.1 and removed in 2.0.0. fread() will only return a single byte of a multibyte char, and so will cause potentially unexpected results/warnings in some cases, and psr7 streams should be used instead. Note that getBinaryContentResourceHandle and getResourceHandle are still available.

    Change in 2.0

    Upgrade to 2.0 to take advantage of the new on-demand parser which parses parts of a message as they're requested. This means reading only the headers from a larger message is as fast as a smaller message because the whole message is no longer parsed (similarly reading just the content and not a message's large attachments is also much faster.)

    Because of the on-demand parsing, starting in 2.0, the passed resource handle or stream must remain open while the returned message object is still in use.

    Old code:

    $handle = fopen('file.mime', 'r');
    $message = $mailParser->parse($handle);         // returned `Message`
    fclose($handle);
    

    New code:

    // attaches the resource handle to the returned `IMessage` if the second parameter
    // is true.  The resource handle is closed when the IMessage is destroyed.
    $message = $mailParser->parse(fopen('file.mime', 'r'), true);
    

    For a more complete list of changes, please visit the 2.0 Upgrade Guide.

    Requirements

    MailMimeParser requires PHP 5.4 or newer. Tested on PHP 5.4, 5.5, 5.6, 7, 7.1, 7.2, 7.3, 7.4, 8.0 and 8.1.

    Usage

    use ZBateson\MailMimeParser\MailMimeParser;
    use ZBateson\MailMimeParser\Message;
    use ZBateson\MailMimeParser\Header\HeaderConsts;
    
    // use an instance of MailMimeParser as a class dependency
    $mailParser = new MailMimeParser();
    
    // parse() accepts a string, resource or Psr7 StreamInterface
    // pass `true` as the second argument to attach the passed $handle and close
    // it when the returned IMessage is destroyed.
    $handle = fopen('file.mime', 'r');
    $message = $mailParser->parse($handle, false);         // returns `IMessage`
    
    // OR: use this procedurally (Message::from also accepts a string,
    // resource or Psr7 StreamInterface
    // true or false as second parameter doesn't matter if passing a string.
    $string = "Content-Type: text/plain\r\nSubject: Test\r\n\r\nMessage";
    $message = Message::from($string, false);
    
    echo $message->getHeaderValue(HeaderConsts::FROM);     // user@example.com
    echo $message
        ->getHeader(HeaderConsts::FROM)                    // AddressHeader
        ->getPersonName();                                 // Person Name
    echo $message->getHeaderValue(HeaderConsts::SUBJECT);  // The email's subject
    echo $message
        ->getHeader(HeaderConsts::TO)                      // also AddressHeader
        ->getAddresses()[0]                                // AddressPart
        ->getName();                                       // Person Name
    echo $message
        ->getHeader(HeaderConsts::CC)                      // also AddressHeader
        ->getAddresses()[0]                                // AddressPart
        ->getEmail();                                      // user@example.com
    
    echo $message->getTextContent();                       // or getHtmlContent()
    
    echo $message->getHeader('X-Foo');                     // for custom or undocumented headers
    
    $att = $message->getAttachmentPart(0);                 // first attachment
    echo $att->getHeaderValue(HeaderConsts::CONTENT_TYPE); // e.g. "text/plain"
    echo $att->getHeaderParameter(                         // value of "charset" part
        'content-type',
        'charset'
    );
    echo $att->getContent();                               // get the attached file's contents
    $stream = $att->getContentStream();                    // the file is decoded automatically
    $dest = \GuzzleHttp\Psr7\stream_for(
        fopen('my-file.ext')
    );
    \GuzzleHttp\Psr7\copy_to_stream(
        $stream, $dest
    );
    // OR: more simply if saving or copying to another stream
    $att->saveContent('my-file.ext');               // writes to my-file.ext
    $att->saveContent($stream);                     // copies to the stream
    
    // close only when $message is no longer being used.
    fclose($handle);
    
    

    Documentation

    Upgrading to 1.x or 2.x

    License

    BSD licensed - please see license agreement.

  • \ZBateson\MbWrapper Readme

    zbateson/mb-wrapper

    Charset conversion and string manipulation wrapper with a large defined set of aliases.

    Build Status
    Code Coverage
    Scrutinizer Code Quality
    Total Downloads
    Latest Stable Version

    The goals of this project are to be:

    • Well written
    • Tested where possible
    • Support as wide a range of charset aliases as possible

    To include it for use in your project, please install via composer:

    composer require zbateson/mb-wrapper
    

    Requirements

    mb-wrapper requires PHP 5.4 or newer. Tested on PHP 5.4, 5.5, 5.6, 7, 7.1, 7.2, 7.3, 7.4 and 8.0 on travis.

    Description

    MbWrapper is intended for use wherever mb* or iconv is used. It scans supported charsets returned by mb_listencodings(), and prefers mb functions, but will fallback to iconv if a charset isn't supported.

    A list of aliased charsets is maintained for both mb* and iconv, where a supported charset exists for an alias. This is useful for mail and http parsing as other systems may report encodings not recognized by mb* or iconv.

    Charset lookup is done by removing non-alphanumeric characters as well, so UTF8 will always be matched to UTF-8, etc...

    Usage

    The following wrapper methods are exposed:

    • mb_convert_encoding, iconv with MbWrapper::convert
    • mb_substr, iconv_substr with MbWrapper::getSubstr
    • mb_strlen, iconv_strlen with MbWrapper::getLength
    • mb_check_encoding, iconv (for verification) with MbWrapper::checkEncoding
    $mbWrapper = new \ZBateson\MbWrapper\MbWrapper();
    $fromCharset = 'ISO-8859-1';
    $toCharset = 'UTF-8';
    
    $mbWrapper->convert('data', $fromCharset, $toCharset);
    $mbWrapper->getLength('data', 'UTF-8');
    $mbWrapper->substr('data', 'UTF-8', 1, 2);
    
    if ($mbWrapper->checkEncoding('data', 'UTF-8')) {
        echo 'Compatible';
    }
    

    License

    BSD licensed - please see license agreement.

  • \ZBateson\StreamDecorators Readme

    zbateson/stream-decorators

    Psr7 stream decorators for character set conversion and common mail format content encodings.

    Tests
    Code Coverage
    Scrutinizer Code Quality
    Total Downloads
    Latest Stable Version

    The goals of this project are to be:

    • Well written
    • Standards-compliant but forgiving
    • Tested where possible

    To include it for use in your project, please install via composer:

    composer require zbateson/stream-decorators
    

    Requirements

    StreamDecorators requires PHP 5.4 or newer. Tested on PHP 5.4, 5.5, 5.6, 7, 7.1, 7.2, 7.3, 7.4 and 8.0.

    Usage

    $stream = GuzzleHttp\Psr7\Utils::streamFor($handle);
    $b64Stream = new ZBateson\StreamDecorators\Base64Stream($stream);
    $charsetStream = new ZBateson\StreamDecorators\CharsetStream($b64Stream, 'UTF-32', 'UTF-8');
    
    while (($line = GuzzleHttp\Psr7\Utils::readLine()) !== false) {
        echo $line, "\r\n";
    }
    
    

    Note that CharsetStream, depending on the target encoding, may return multiple bytes when a single 'char' is read. If using php's 'fread', this will result in a warning:

    'read x bytes more data than requested (xxxx read, xxxx max) - excess data will be lost

    This is because the parameter to 'fread' is bytes, and so when CharsetStream returns, say, 4 bytes representing a single UTF-32 character, fread will truncate to the first byte when requesting '1' byte. It is recommended to not convert to a stream handle (with StreamWrapper) for this reason when using CharsetStream.

    The library consists of the following Psr\Http\Message\StreamInterface implementations:

    • ZBateson\StreamDecorators\QuotedPrintableStream - decodes on read and encodes on write to quoted-printable
    • ZBateson\StreamDecorators\Base64Stream - decodes on read and encodes on write to base64
    • ZBateson\StreamDecorators\UUStream - decodes on read, encodes on write to uu-encoded
    • ZBateson\StreamDecorators\CharsetStream - encodes from $streamCharset to $stringCharset on read, and vice-versa on write
    • ZBateson\StreamDecorators\NonClosingStream - overrides close() and detach(), and simply unsets the attached stream without closing it
    • ZBateson\StreamDecorators\ChunkSplitStream - splits written characters into lines of $lineLength long (stream implementation of php's chunk_split)
    • ZBateson\StreamDecorators\PregReplaceFilterStream - calls preg_replace on with passed arguments on every read() call
    • ZBateson\StreamDecorators\SeekingLimitStream - similar to GuzzleHttp's LimitStream, but maintains an internal current read position, seeking to it when read() is called, and seeking back to the wrapped stream's position after reading

    QuotedPrintableStream, Base64Stream and UUStream's constructors take a single argument of a StreamInterface.
    CharsetStreams's constructor also takes $streamCharset and $stringCharset as arguments respectively, ChunkSplitStream
    optionally takes a $lineLength argument (defaults to 76) and a $lineEnding argument (defaults to CRLF).
    PregReplaceFilterStream takes a $pattern argument and a $replacement argument. SeekingLimitStream takes optional
    $limit and $offset parameters, similar to GuzzleHttp's LimitStream.

    License

    BSD licensed - please see license agreement.

© 2022 Bruce Wells
Search Namespaces \ Classes
Configuration