Yet another BibTeX parser
Clone or download
Renan
Renan Revert "commit composer.lock"
This reverts commit df0de91.
Latest commit dded1a6 Aug 17, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github drop php-cs-fixer dependency Aug 16, 2018
src applies phpstan fixes Aug 15, 2018
tests applies phpstan fixes Aug 15, 2018
.editorconfig update cs definitions Oct 24, 2017
.gitignore Revert "commit composer.lock" Aug 17, 2018
.php_cs adopt Symfony CS style rules Dec 12, 2017
.travis.yml prepare composer and dotfiles for PHP 5 Nov 19, 2017
LICENSE happy new year Oct 13, 2017
README.md prepare release 2.0.0 Nov 25, 2017
composer.json drop php-cs-fixer dependency Aug 16, 2018
phpunit.xml enable codecov Nov 1, 2017

README.md

PHP BibTeX Parser 2.x

This is a BibTeX parser written in PHP.

BibTeX logo PHP logo

Build Status codecov

You are browsing the documentation of BibTeX Parser 2.x, the latest version.

Documentation for version 1.x is available here.

Table of contents

Installing

composer require renanbr/bibtex-parser ^2

Usage

use RenanBr\BibTexParser\Listener;
use RenanBr\BibTexParser\Parser;

require 'vendor/autoload.php';

$bibtex = <<<BIBTEX
@article{einstein1916relativity,
  title={Relativity: The Special and General Theory},
  author={Einstein, Albert},
  year={1916}
}
BIBTEX;

$parser = new Parser();          // Create a Parser
$listener = new Listener();      // Create and configure a Listener
$parser->addListener($listener); // Attach the Listener to the Parser
$parser->parseString($bibtex);   // or parseFile('/path/to/file.bib')
$entries = $listener->export();  // Get processed data from the Listener

print_r($entries);

This will output:

Array
(
    [0] => Array
        (
            [type] => article
            [citation-key] => einstein1916relativity
            [title] => Relativity: The Special and General Theory
            [author] => Einstein, Albert
            [year] => 1916
        )
)

Vocabulary

BibTeX is all about "entry", "tag's name" and "tag's content".

A BibTeX entry consists of the type (the word after @), a citation-key and a number of tags which define various characteristics of the specific BibTeX entry. (...) A BibTeX tag is specified by its name followed by an equals-sign and the content.

Source: http://www.bibtex.org/Format/

Note: This library considers "type" and "citation-key" as tags. This behavior can be changed implementing your own Listener (more info at the end of this document).

Processors

Processor is a callable that receives an entry as argument and returns a modified entry.

This library contains three main parts:

  • Parser class, responsible for detecting units inside a BibTeX input;
  • Listener class, responsible for gathering units and transforming them into a list of entries;
  • Processor classes, responsible for manipulating entries.

Despite you can't configure the Parser, you can append as many Processor as you want to the Listener through Listener::addProcessor() before exporting the contents. Be aware that Listener provides, by default, these features:

  • Found entries are reachable through Listener::export() method;
  • Tag content concatenation;
    • e.g. hello # " world" tag's content will generate hello world string
  • Tag content abbreviation handling;
    • e.g. @string{foo="bar"} @misc{bar=foo} will make $entries[1]['bar'] assume bar as value
  • Publication's type is exposed as type tag;
  • Citation key is exposed as citation-key tag;
  • Original entry text is exposed as _original tag.

This project is shipped with some useful processors.

Tag name case

In BibTeX the tag's names aren't case-sensitive. This library exposes entries as array, in which keys are case-sensitive. To avoid this misunderstanding, you can force the tags' name character case using TagNameCaseProcessor.

use RenanBr\BibTexParser\Processor\TagNameCaseProcessor;

$listener->addProcessor(new TagNameCaseProcessor(CASE_UPPER)); // or CASE_LOWER
@article{
  title={BibTeX rocks}
}
Array
(
    [0] => Array
        (
            [TYPE] => article
            [TITLE] => BibTeX rocks
        )
)

Authors and editors

BibTeX recognizes four parts of an author's name: First Von Last Jr. If you would like to parse the author and editor tags included in your entries, you can use the NamesProcessor class.

use RenanBr\BibTexParser\Processor\NamesProcessor;

$listener->addProcessor(new NamesProcessor());
@article{
  title={Relativity: The Special and General Theory},
  author={Einstein, Albert}
}
Array
(
    [0] => Array
        (
            [type] => article
            [title] => Relativity: The Special and General Theory
            [author] => Array
                (
                    [0] => Array
                        (
                            [first] => Albert
                            [von] =>
                            [last] => Einstein
                            [jr] =>
                        )
                )
        )
)

Keywords

The keywords tag contains a list of expressions represented as string, you might want to read them as an array instead.

use RenanBr\BibTexParser\Processor\KeywordsProcessor;

$listener->addProcessor(new KeywordsProcessor());
@misc{
  title={The End of Theory: The Data Deluge Makes the Scientific Method Obsolete},
  keywords={big data, data deluge, scientific method}
}
Array
(
    [0] => Array
        (
            [type] => misc
            [title] => The End of Theory: The Data Deluge Makes the Scientific Method Obsolete
            [keywords] => Array
                (
                    [0] => big data
                    [1] => data deluge
                    [2] => scientific method
                )
        )
)

LaTeX to unicode

BibTeX files store LaTeX contents. You might want to read them as unicode instead. The LatexToUnicodeProcessor class solves this problem, but before adding the processor to the listener you must:

use RenanBr\BibTexParser\Processor\LatexToUnicodeProcessor;

$listener->addProcessor(new LatexToUnicodeProcessor());
@article{
  title={Caf\\'{e}s and bars}
}
Array
(
    [0] => Array
        (
            [type] => article
            [title] => Cafés and bars
        )
)

Note: Order matters, add this processor as the last.

Custom

The Listener::addProcessor() method expects a callable as argument. In the example shown below, we append the text with laser to the title tags for all entries.

$listener->addProcessor(function (array $entry) {
    $entry['title'] .= ' with laser';
    return $entry;
});
@article{
  title={BibTeX rocks}
}
Array
(
    [0] => Array
        (
            [type] => article
            [title] => BibTeX rocks with laser
        )
)

Handling errors

This library throws two types of exception: ParserException and ProcessorException. The first one may happen during the data extraction. When it occurs it probably means the parsed BibTeX isn't valid. The second exception may be throwed during the data processing. When it occurs it means the listener's processors can't handle properly the data found. Both implement ExceptionInterface.

use RenanBr\BibTexParser\Exception\ExceptionInterface;
use RenanBr\BibTexParser\Exception\ParserException;
use RenanBr\BibTexParser\Exception\ProcessorException;

try {
    // ... parser and listener configuration

    $parser->parseFile('/path/to/file.bib');
    $entries = $listener->export();
} catch (ParserException $exception) {
    // The BibTeX isn't valid
} catch (ProcessorException $exception) {
    // Listener's processors aren't able to handle data found
} catch (ExceptionInterface $exception) {
    // Alternatively, you can use this exception to catch all of them at once
}

Advanced usage

The core of this library is constituted of these classes:

  • RenanBr\BibTexParser\Parser: responsible for detecting units inside a BibTeX input;
  • RenanBr\BibTexParser\ListenerInterface: responsible for treating units found.

You can attach listeners to the parser through Parser::addListener(). The parser is able to detect BibTeX units, such as "type", "tag's name", "tag's content". As the parser finds an unit, listeners are triggered.

You can code your own listener! All you have to do is handle units.

interface RenanBr\BibTexParser\ListenerInterface
{
    /**
     * Called when an unit is found.
     *
     * @param string $text    The original content of the unit found.
     *                        Escape character will not be sent.
     * @param string $type    The type of unit found.
     *                        It can assume one of Parser's constant value.
     * @param array  $context Contains details of the unit found.
     */
    public function bibTexUnitFound($text, $type, array $context);
}

$type may assume one of these values:

  • Parser::TYPE
  • Parser::CITATION_KEY
  • Parser::TAG_NAME
  • Parser::RAW_TAG_CONTENT
  • Parser::BRACED_TAG_CONTENT
  • Parser::QUOTED_TAG_CONTENT
  • Parser::ENTRY

$context is an array with these keys:

  • offset contains the $text's beginning position. It may be useful, for example, to seek on a file pointer;
  • length contains the original $text's length. It may differ from string length sent to the listener because may there are escaped characters.