pax_global_header00006660000000000000000000000064137034416200014512gustar00rootroot0000000000000052 comment=8735708426066b791c2a6d40329050d5cf31385c easyrdf-1.0.0/000077500000000000000000000000001370344162000131455ustar00rootroot00000000000000easyrdf-1.0.0/.travis.yml000066400000000000000000000005371370344162000152630ustar00rootroot00000000000000language: php matrix: include: - php: 7.1 dist: trusty - php: 7.2 dist: trusty - php: 7.3 dist: trusty - php: 7.4 dist: trusty sudo: required before_install: - sudo apt-get update -qq - sudo apt-get install -qq graphviz install: make composer-install script: - make lint - make cs - make test-lib easyrdf-1.0.0/CHANGELOG.md000066400000000000000000000400301370344162000147530ustar00rootroot00000000000000EasyRdf 1.0.0 ============= Major new features ------------------ * Minimum version of PHP is now PHP version 7.1, PHP 5.x is no-longer supported * Usage without composer is not supported anymore * Library is loaded via PSR-4 autoloader now * The Redland PHP extension is no-longer supported Enhancements ------------ * `$graph->isA()` can take full IRIs as second parameter (only qname was accepted earlier, see issue #215) * `Accept` HTTP-header depends on SPARQL-query type (see issues #231, #226) * It is possible to set alternate default `Resource` class via `Graph::setDefaultResourceClass()` (see issue #243) * When calling `Graph::load()` set the HTTP Accept header to the desired format * The RDF/PHP and RDF/JSON specifications were added to the documentation * Added text/xml and application/xml MIME types to RDF/XML format * Added additional namespaces from W3C RDFa context * Speeding up the turtle parser by optimising the mb_substr calls * Added support for compressed response body in HTTP client * Updated to PHP CodeSniffer v3 * Updated to Sami v4 for API documentation * Updated to PHPUnit v7 API changes ----------- * Classes are renamed like this: `EasyRdf_Parser_Turtle` → `EasyRdf\Parser\Turtle`. With a single exception: `EasyRdf_Namespace` → `EasyRdf\RdfNamespace` (because `namespace` is a keyword in PHP) * EasyRdf expects HTTP-client objects compatible with ZendFramework 2.x instead of 1.x now. (zend-http is added to require-dev so tests for it are always run) * `Resource` implements `ArrayAccess` interface now (see #242) * Now using PHP Type Hints in some classes, which avoids having to do type checks * Serialiser class is now abstract * Implement the ArrayAccess Interface in Resource Bug Fixes --------- * Fixes that add compatibility for PHP 7.4 * Unicode-strings are properly encoded in n-triples documents (see #219) * `RdfPhp` parser validates its input (see #227) * Timeout is applied to response-times, not only connection-times (see #202) * `$graph->get()` is reliable after `$graph->delete()` now (see #239, #241) * Fix for running Graphviz tests against newer versions of Graphviz * Fixed for when the HTTP server doesn't return Reason-Phrase in the status (see #321) * Corrections to RDF format URIs * Fixes for format guessing, so it works with SPARQL-style PREFIX and BASE * Turtle serialiser improvements and fixes * Fix for unescaping URIs while parsing ntriples * Fixed encoding of unicode literals in ntriples Changes to Examples ------------------- * Added index.php to examples folder, to make them easier to navigate * Removed artistinfo.php example (BBC Music no longer publishes RDF) * Replaced Dbpedialite Villages example with Wikidata Villages example * Added Open Graph Protocol example * Changed default URI for converter example * Fixed namespace for dbpedia categories * Fixes for UK Postcode example and changed it to use Open Street Map EasyRdf 0.9.1 ============= Bug Fixes --------- * Support timeouts for HTTP requests where the server takes a long time to answer. Fixes #202 * Fixed Google Map on UK Postcode example EasyRdf 0.9.0 ============= Major new features ------------------ * Framing support in `EasyRdf_Serialiser_JsonLd` * JSON-LD Parser API changes ----------- * `EasyRdf_Literal_Decimal` returns strings, instead of floats to avoid losing precision (see issue #178) * `EasyRdf_Literal_Decimal` requires input-strings which conform to `xs:decimal` format * `EasyRdf_GraphStore` supports operations over default graph now * `EasyRdf_Literal` typed as `xs:double` is used for PHP-floats instead of `EasyRdf_Literal_Decimal` * Exceptions thrown from `EasyRdf_Graph::resource()` use different message-texts now (see issue #159) Enhancements ------------ * Synced list of default namespaces against [RDFa Core Initial Context](http://www.w3.org/2011/rdfa-context/rdfa-1.1) rev.2014-01-17 * Added support for empty prefixes (see issue #183) * `EasyRdf_Graph::newAndLoad` throws `EasyRdf_Http_Exception` in case of failure, which gives access to status and response-body. (see issue #149) * `EasyRdf_Graph` and `EasyRdf_Resource` have 'typesAsResources()' methods now Bug Fixes --------- * Fix for Turtle serialisation of FALSE (see issue #179) * Fix for edge-case in RDF/XML serialisation (see issue #186) * SPARQL-queries against endpoints which have query-params in their URL (see issue #184) * Float values are properly handled if locale with "other" separator is active * Fixed parsing of Turtle-documents with higher utf-8 characters (see issue #195) * Namespace-prefixes are compliant with RDFXML QName spec (see issue #185) * `EasyRdf_Namespace` won't generate "short" names with "/" in them anymore (see issue #115) * `EasyRdf_Parser_RdfXml` respects "base" specified for the document (see issue #157) * HTML documents are correctly detected now, not as "n-triples" (see issue #206) * Accept-headers are formatted in locale-independent fashion now (see issue #208) EasyRdf 0.8.0 ============= Major new features ------------------ * Now PSR-2 compliant * Added RDFa parser * Added SPARQL Update support to `EasyRdf_Sparql_Client` API changes ----------- * `is_a()` has been renamed to `isA()` * `isBnode()` has been renamed to `isBNode()` * `getNodeId()` has been renamed to `getBNodeId()` * Added a `$value` property to `hasProperty()` * Renamed `toArray()` to `toRdfPhp()` * Renamed `count()` to `countValues()` in `EasyRdf_Graph` and `EasyRdf_Resource` * Made passing a URI to `delete()` behave more like `all()` and `get()` - you must enclose in `<>` * `dump(true)` has changed to `dump('html')` * `getUri()` in `EasyRdf_Sparql_Client` has been renamed to `getQueryUri()` Enhancements ------------ * Added `EasyRdf_Container` class to help iterate through `rdf:Alt`, `rdf:Bag` and `rdf:Seq` * Added `EasyRdf_Collection` class to help iterate through `rdf:List` * Added `EasyRdf_Literal_HTML` and `EasyRdf_Literal_XML` * Changed formatting of `xsd:dateTime` from `DateTime::ISO8601` to `DateTime::ATOM` * Added `rss:title` to the list of properties that `label()` will check for * Added support for serialising containers to the RDF/XML serialiser * Added getGraph method to `EasyRdf_Resource` * Turtle parser improvements * Added the `application/n-triples` MIME type for the N-Triples format * Added support to `EasyRdf_Namespace` for expanding `a` to `rdf:type` * Added `listNamedGraphs()` function to `EasyRdf_Sparql_Client` * Added line and column number to exceptions in the built-in parsers Bug Fixes --------- * Fixed bug in `EasyRdf_Namespace::expand()` (see issue #114) * Fix for dumping SPARQL SELECT query with unbound result (see issue #112) * Sesame compatibility : avoid duplicate Content-Length header * Fix for for passing objects of type DateTime to $graph->add() (see issue #119) * Fix for SPARQL queries longer than 2KB (see issue #85) * Fix for dumping literal with unshortenable datatype uri (see issue #120) * Fix for getting default mime type or extension when there isn't one * Fix for missing trailing slash the HTTP client EasyRdf 0.7.2 ============= Enhancements ------------ * Removed automatic registration of ARC2 and librdf parsers and serialisers ** You must now specifically choose the parser or serialiser * Refactored `EasyRdf_Literal` with datatypes so that it preserves exact value * Changed Turtle serialiser to not escape Unicode characters unnecessarily * Fix for escaping literals objects in Turtle serialiser * Added a new static function `newAndLoad()` to `EasyRdf_Graph` * Added setters for each of the components of the URI to the class `EasyRdf_ParsedUri` * Added option to the converter example, to allow raw output, without any HTML Bug Fixes --------- * Fixed broken Redland parser (thanks to Jon Phipps) * Fix for serialising two bnodes that reference each other in Turtle * Added support for parsing literals with single quotes in Turtle * Removed require for EasyRdf/Exception.php * Fix for serialising `EasyRdf_Literal_DateTime` to Turtle * Fix for serialising Turtle literals with a shorthand syntax * Several typo fixes and minor corrections EasyRdf 0.7.1 ============= Enhancements ------------ * Changed minimum version of PHPUnit to 3.5.15 * Added RDFa namespace * Added Open Graph Protocol namespace * Made improvements to formatting of the Turtle serialiser * Added new splitUri() function to EasyRdf_Namespace * Made improvements to format guessing Bug Fixes --------- * Fix for RDF/XML parser not returning the number of triples * Added re-mapping of b-nodes to N-Triples and Redland parsers EasyRdf 0.7.0 ============= API Changes ----------- * You must now wrap full property URIs in angle brackets Major new features ------------------ * Added a new pure-PHP Turtle parser * Added basic property-path support for traversing graphs * Added support for serialising to the GraphViz dot format (and generating images) * Added a new class `EasyRdf_ParsedUri` - a RFC3986 compliant URI parser Enhancements ------------ * The load() function in `EasyRdf_Graph` no-longer takes a $data argument * The parse() and load() methods, now return the number of triples parsed * Added count() method to `EasyRdf_Resource` and `EasyRdf_Graph` * Added localName() method to `EasyRdf_Resource` * Added htmlLink() method to `EasyRdf_Resource` * Added methods deleteResource() and deleteLiteral() to `EasyRdf_Graph` * Added support for guessing the file format based on the file extension * Performance improvements to built-in serialisers Environment changes ------------------- * Added PHP Composer description to the project * Now properly PSR-0 autoloader compatible * New minimum version of PHP is 5.2.8 * Changed test suite to require PHPUnit 3.6 * Changed from Phing to GNU Make based build system * Added automated testing of the examples Bug Fixes --------- * Fix for loading https:// URLs * Fix for storing the value 0 in a `EasyRdf_Graph` * Fix for HTTP servers that return relative URIs in the Location header * Fix for Literals with languages in the SPARQL Query Results XML Format * Fix for SPARQL servers that put extra whitespace into the XML result * Fix for the httpget.php example in PHP 5.4+ EasyRdf 0.6.3 ============= * Added $graph->parseFile() method. * Added support for SSL (https) to the built-in HTTP client * Fixes for HTTP responses with a charset parameter in the Content Type. * Improved error handling and empty documents in JSON and rapper parsers. * Added connivence class for xsd:hexBinary literals: - `EasyRdf_Literal_HexBinary` * Made EasyRdf more tolerant of 'badly serialised bnodes' * Fix for SPARQL servers that return charset in the MIME Type. * Fix for using xml:lang in SPARQL 1.1 Query Results JSON Format * Changed datetime ISO formatting to use 'Z' instead of +0000 for UTC dateTimes * Added the namespace for 'The Cert Ontology' to EasyRdf. EasyRdf 0.6.2 ============= * Bug fix for missing triples in the RDF/XML serialiser. * Added countTriples() method to `EasyRdf_Graph`. * Re-factored the mechanism for mapping RDF datatypes to PHP classes. * Added subclasses of `EasyRdf_Literal` for various XSD datatypes: - `EasyRdf_Literal_Boolean` - `EasyRdf_Literal_Date` - `EasyRdf_Literal_DateTime` - `EasyRdf_Literal_Decimal` - `EasyRdf_Literal_Integer` * Made the Redland based parser write triples directly to `EasyRdf_Graph` * Added support for datatypes and languages in the `EasyRdf_Parser_Ntriples` parser. * Fix for parsing XML Literals in RDF/XML EasyRdf 0.6.1 ============= * Updated API documentation for new classes and methods added in 0.6.0 * Added a description to the top of the source code for each example. * Changed the generated bnode identifier names from eidXXX to genidXXX. * Implemented inlining of resources in the RDF/XML serialiser. * Added new reversePropertyUris() method to `EasyRdf_Graph` and `EasyRdf_Resource`. * Added addType() and setType() to `EasyRdf_Resource`. * Added a textarea to the converter example. * Added support for parsing the json-triples format. * Renamed `EasyRdf_SparqlClient` to `EasyRdf_Sparql_Client` * Renamed `EasyRdf_SparqlResult` to `EasyRdf_Sparql_Result` * Fix for $graph->isEmpty() failing after adding and deleting some triples * Added new `EasyRdf_DatatypeMapper` class that allows you to map RDF datatypes to PHP classes. * Renamed guessDatatype() to getDatatypeForValue() in `EasyRdf_Literal`. * Added getResource() and allResources() to `EasyRdf_Graph` and `EasyRdf_Resource` * Implemented value casting in literals based on the datatype. EasyRdf 0.6.0 ============= * Major re-factor of the way data is stored internally in `EasyRdf_Graph`. * Parsing and serialising is now much faster and will enable further optimisations. * API is mostly backwards-compatible apart from: - Changed inverse property operator from - to ^ to match Sparql 1.1 property paths. - New `EasyRdf_Graphs` will not automatically be loaded on creation You must now call $graph->load(); - Setting the default HTTP client is now part of a new `EasyRdf_Http` class - It is no-longer possible to add multiple properties at once using an associative array. * Added methods to `EasyRdf_Graph` for direct manipulation of triples. * Added new `EasyRdf_GraphStore` - class for fetching, saving and deleting graphs to a Graph Store over HTTP. * Added new `EasyRdf_SparqlClient` and `EasyRdf_SparqlResult` - class for querying a SPARQL endpoint over HTTP. * Added q values for each Mime-Type associated with an `EasyRdf_Format`. * New example demonstrating integration with the Zend Framework. * New `EasyRdf_HTTP_MockClient` class makes testing easier. EasyRdf 0.5.2 ============= * Added a built-in RDF/XML parser * Made the RDF/XML serialiser use the rdf:type to open tags * Added support for comments in the N-Triples parser * Added new resolveUriReference() function to `EasyRdf_Utils` * Added the application/rdf+json and text/rdf+n3 mime types EasyRdf 0.5.1 ============= * Bug fixes for PHP 5.2 EasyRdf 0.5.0 ============= * Added support for inverse properties. * Updated RDF/XML and Turtle serialisers to create new namespaces if possible. * Added new is_a($type) method to `EasyRdf_Resource`. * Added support for passing an array of properties to the get() method. * Added primaryTopic() method to `EasyRdf_Resource`. * The function label() in `EasyRdf_Resource` will no longer attempted to shorten the URI, if there is no label available. * Resource types are now stored as resources, instead of shortened URIs. * Added support for deleting a specific value for property to `EasyRdf_Resource`. * Properties and datatypes are now stored as full URIs and not converted to qnames during import. * Change the TypeMapper to store full URIs internally. * Added bibo and geo to the set of default namespaces. * Improved bnode links in dump format * Fix for converting non-string `EasyRdf_Literal` to string. * Created an example that resolves UK postcodes using uk-postcodes.com. EasyRdf 0.4.0 ============= * Moved source code to Github * Added an `EasyRdf_Literal` class * Added proper support for Datatypes and Languages * Added built-in RDF/XML serialiser * Added built-in Turtle serialiser * Added a new `EasyRdf_Format` class to deal with mime types etc. * finished a major refactoring of the Parser/Serialiser registration * removed all parsing related code from `EasyRdf_Graph` * Added a basic serialisation example * Added additional common namespaces * Test fixes EasyRdf 0.3.0 ============= * Generated Wiki pages from phpdoc * Filtering of literals by language * Moved parsers into `EasyRdf_Parser_XXX` namespace * Added support for serialisation * Wrote RDF generation example (foafmaker.php) * Added built-in ntriples parser/generator * Added built-in RDF/PHP serialiser * Added built-in RDF/JSON serialiser * Added SKOS and RSS to the set of default namespaces. EasyRdf 0.2.0 ============= * Added support for Redland PHP bindings * Added support for n-triples document type. * Improved blank node handing and added newBNode() method to `EasyRdf_Graph`. * Add option to `EasyRdf_RapperParser` to choose location of rapper command * Added Rails style HTML tag helpers to examples to make them simpler EasyRdf 0.1.0 ============= * First public release * Support for ARC2 and Rapper * Built-in HTTP Client * API Documentation * PHP Unit tests for every class. * Several usage examples easyrdf-1.0.0/CODE_OF_CONDUCT.md000066400000000000000000000121421370344162000157440ustar00rootroot00000000000000 # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders 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, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at njh@aelius.com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. easyrdf-1.0.0/DEVELOPER.md000066400000000000000000000013201370344162000150100ustar00rootroot00000000000000Contributing to EasyRdf ======================= Contributions to the EasyRdf codebase are welcome using the usual Github pull request workflow. To run the code style checker: ``` make cs ``` You can run the PHP unit test suite with: ``` make test-lib ``` Unit tests are automatically run after being received by Github: http://ci.aelius.com/job/easyrdf/ The tests for the examples are run separately: http://ci.aelius.com/job/easyrdf-examples/ Notes ----- * Please ask on the [mailing list] before starting work on any significant changes * Please write tests for any new features or bug fixes. The tests should be checked in the same commit as the code. [mailing list]:http://groups.google.com/group/easyrdf easyrdf-1.0.0/LICENSE.md000066400000000000000000000553261370344162000145640ustar00rootroot00000000000000LICENSE ======= Copyright (c) 2009-2020 Nicholas J Humfrey. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of the author 'Nicholas J Humfrey" may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OTHER LICENSES ============== Parts of this program or documentation is available under different licensing terms. These are as following. The appendix in the documentation about RDF formats (APPENDIX A) is a derivative work under CC-BY-SA-3.0. It consists of two documents: 1. The RDF/PHP Specification was written/edited 2008 by Ian Davis and Keith Alexander 2. The RDF/JSON Specification was written/edited 2007, 2008 by Keith Alexander, Danny Ayers, Sam Tunnicliffe, Fellahst, Ian Davis and Robman These two documents have been translated 2014 into markdown by hakre. Creative Commons Attribution-ShareAlike 3.0 Unported ==================================================== CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. License THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 1. Definitions a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. c. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. d. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. e. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. f. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. g. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. h. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. i. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. j. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. k. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, d. to Distribute and Publicly Perform Adaptations. e. For the avoidance of doubt: i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. c. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. d. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. 5. Representations, Warranties and Disclaimer UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. Termination a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 8. Miscellaneous a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. Creative Commons Notice Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License. Creative Commons may be contacted at http://creativecommons.org/. easyrdf-1.0.0/README.md000066400000000000000000000124141370344162000144260ustar00rootroot00000000000000EasyRdf ======= [![Build Status](https://travis-ci.com/easyrdf/easyrdf.svg?branch=master)](https://travis-ci.com/easyrdf/easyrdf) EasyRdf is a PHP library designed to make it easy to consume and produce [RDF]. It was designed for use in mixed teams of experienced and inexperienced RDF developers. It is written in Object Oriented PHP and has been tested extensively using PHPUnit. After parsing EasyRdf builds up a graph of PHP objects that can then be walked around to get the data to be placed on the page. Dump methods are available to inspect what data is available during development. Data is typically loaded into an [`EasyRdf\Graph`] object from source RDF documents, loaded from the web via HTTP. The [`EasyRdf\GraphStore`] class simplifies loading and saving data to a SPARQL 1.1 Graph Store. SPARQL queries can be made over HTTP to a Triplestore using the [`EasyRdf\Sparql\Client`] class. `SELECT` and `ASK` queries will return an [`EasyRdf\Sparql\Result`] object and `CONSTRUCT` and `DESCRIBE` queries will return an [`EasyRdf\Graph`] object. ### Example ### ```php $foaf = new \EasyRdf\Graph("http://njh.me/foaf.rdf"); $foaf->load(); $me = $foaf->primaryTopic(); echo "My name is: ".$me->get('foaf:name')."\n"; ``` Downloads --------- The latest _stable_ version of EasyRdf can be [downloaded from the EasyRdf website]. Links ----- * [EasyRdf Homepage](https://www.easyrdf.org/) * [API documentation](https://www.easyrdf.org/docs/api) * [Change Log](https://github.com/easyrdf/easyrdf/blob/master/CHANGELOG.md) * [Source Code](https://github.com/easyrdf/easyrdf) * [Issue Tracker](https://github.com/easyrdf/easyrdf/issues) Requirements ------------ * PHP 7.1 or higher Features -------- * API documentation written in `phpdoc` * Extensive unit tests written using `phpunit` * Built-in parsers and serialisers: RDF/JSON, N-Triples, RDF/XML, Turtle * Optional parsing support for: [ARC2], [rapper] * Optional support for [`Zend\Http\Client`] * No required external dependancies upon other libraries (PEAR, Zend, etc...) * Complies with Zend Framework coding style. * Type mapper - resources of type `foaf:Person` can be mapped into PHP object of class `Foaf_Person` * Support for visualisation of graphs using [GraphViz] * Comes with a number of examples List of Examples ---------------- * [`basic.php`](/examples/basic.php#slider) - Basic "Hello World" type example * [`basic_sparql.php`](/examples/basic_sparql.php#slider) - Example of making a SPARQL `SELECT` query * [`converter.php`](/examples/converter.php#slider) - Convert RDF from one format to another * [`dump.php`](/examples/dump.php#slider) - Display the contents of a graph * [`foafinfo.php`](/examples/foafinfo.php#slider) - Display the basic information in a FOAF document * [`foafmaker.php`](/examples/foafmaker.php#slider) - Construct a FOAF document with a choice of serialisations * [`graph_direct.php`](/examples/graph_direct.php#slider) - Example of using `EasyRdf\Graph` directly without `EasyRdf\Resource` * [`graphstore.php`](/examples/graphstore.php#slider) - Store and retrieve data from a SPARQL 1.1 Graph Store * [`graphviz.php`](/examples/graphviz.php#slider) - GraphViz rendering example * [`html_tag_helpers.php`](/examples/html_tag_helpers.php#slider) - Rails Style html tag helpers to make the EasyRdf examples simpler * [`httpget.php`](/examples/httpget.php#slider) - No RDF, just test `EasyRdf\Http\Client` * [`open_graph_protocol.php`](/examples/open_graph_protocol.php#slider) - Extract Open Graph Protocol metadata from a webpage * [`serialise.php`](/examples/serialise.php#slider) - Basic serialisation example * [`sparql_queryform.php`](/examples/sparql_queryform.php#slider) - Form to submit SPARQL queries and display the result * [`uk_postcode.php`](/examples/uk_postcode.php#slider) - Example of resolving UK postcodes using uk-postcodes.com * [`wikidata_villages.php`](/examples/wikidata_villages.php#slider) - Fetch and information about villages in Fife from Wikidata * [`zend_framework.php`](/examples/zend_framework.php#slider) - Example of using `Zend\Http\Client` with EasyRdf Running Examples ---------------- The easiest way of trying out some of the examples is to use the PHP command to run a local web server on your computer. ``` php -S localhost:8080 -t examples ``` Then open the following URL in your browser: http://localhost:8080/ Licensing --------- The EasyRdf library and tests are licensed under the [BSD-3-Clause] license. The examples are in the public domain, for more information see [UNLICENSE]. [`EasyRdf\Graph`]:https://www.easyrdf.org/docs/api/EasyRdf\Graph.html [`EasyRdf\GraphStore`]:https://www.easyrdf.org/docs/api/EasyRdf\GraphStore.html [`EasyRdf\Sparql\Client`]:https://www.easyrdf.org/docs/api/EasyRdf\Sparql\Client.html [`EasyRdf\Sparql\Result`]:https://www.easyrdf.org/docs/api/EasyRdf\Sparql\Result.html [ARC2]:https://github.com/semsol/arc2/ [BSD-3-Clause]:https://www.opensource.org/licenses/BSD-3-Clause [downloaded from the EasyRdf website]:https://www.easyrdf.org/downloads [GraphViz]:https://www.graphviz.org/ [rapper]:http://librdf.org/raptor/rapper.html [RDF]:https://en.wikipedia.org/wiki/Resource_Description_Framework [SPARQL 1.1 query language]:https://www.w3.org/TR/sparql11-query/ [UNLICENSE]:https://unlicense.org/ [`Zend\Http\Client`]:https://docs.zendframework.com/zend-http/client/intro/ easyrdf-1.0.0/composer.json000066400000000000000000000025501370344162000156710ustar00rootroot00000000000000{ "name": "easyrdf/easyrdf", "version": "1.0.0", "description": "EasyRdf is a PHP library designed to make it easy to consume and produce RDF.", "type": "library", "keywords": ["RDF", "Semantic Web", "Linked Data", "Turtle", "RDFa", "SPARQL"], "homepage": "http://www.easyrdf.org/", "license": "BSD-3-Clause", "authors": [ { "name": "Nicholas Humfrey", "email": "njh@aelius.com", "homepage": "http://www.aelius.com/njh/", "role": "Developer" }, { "name": "Alexey Zakhlestin", "email": "indeyets@gmail.com", "homepage": "http://indeyets.ru/", "role": "Developer" } ], "support": { "forum": "http://groups.google.com/group/easyrdf/", "issues": "http://github.com/easyrdf/easyrdf/issues" }, "require": { "php": ">=7.1.0", "ext-mbstring": "*", "ext-pcre": "*" }, "suggest": { "ml/json-ld": "~1.0", "semsol/arc2": "~2.2" }, "require-dev": { "ml/json-ld": "~1.0", "phpunit/phpunit": "^7", "sami/sami": "^4", "semsol/arc2": "~2.2", "squizlabs/php_codesniffer": "3.*", "zendframework/zend-http": "~2.3" }, "autoload": { "psr-4": { "EasyRdf\\": "lib" } } } easyrdf-1.0.0/doap.php000066400000000000000000000046121370344162000146040ustar00rootroot00000000000000homepage.'doap.rdf'); $easyrdf = $doap->resource('#easyrdf', 'doap:Project', 'foaf:Project'); $easyrdf->addLiteral('doap:name', 'EasyRDF'); $easyrdf->addLiteral('doap:shortname', 'easyrdf'); $easyrdf->addLiteral('doap:revision', $composer->version); $easyrdf->addLiteral('doap:shortdesc', $composer->description, 'en'); $easyrdf->addResource('doap:homepage', $composer->homepage); $easyrdf->addLiteral('doap:programming-language', 'PHP'); $easyrdf->addLiteral( 'doap:description', 'EasyRdf is a PHP library designed to make it easy to consume and produce RDF. '. 'It was designed for use in mixed teams of experienced and inexperienced RDF developers. '. 'It is written in Object Oriented PHP and has been tested extensively using PHPUnit.', 'en' ); $easyrdf->addResource('doap:license', 'http://usefulinc.com/doap/licenses/bsd'); $easyrdf->addResource('doap:download-page', 'http://github.com/easyrdf/easyrdf/downloads'); $easyrdf->addResource('doap:bug-database', 'http://github.com/easyrdf/easyrdf/issues'); $easyrdf->addResource('doap:mailing-list', 'http://groups.google.com/group/easyrdf'); $easyrdf->addResource('doap:category', 'http://dbpedia.org/resource/Resource_Description_Framework'); $easyrdf->addResource('doap:category', 'http://dbpedia.org/resource/PHP'); $easyrdf->addResource('doap:category', 'http://www.dbpedialite.org/things/24131#id'); $easyrdf->addResource('doap:category', 'http://www.dbpedialite.org/things/53847#id'); $repository = $doap->newBNode('doap:GitRepository'); $repository->addResource('doap:browse', 'http://github.com/easyrdf/easyrdf'); $repository->addResource('doap:location', 'git://github.com/easyrdf/easyrdf.git'); $easyrdf->addResource('doap:repository', $repository); $njh = $doap->resource('http://njh.me/', 'foaf:Person'); $njh->addLiteral('foaf:name', 'Nicholas J Humfrey'); $njh->addResource('foaf:homepage', 'http://www.aelius.com/njh/'); $easyrdf->add('doap:maintainer', $njh); $easyrdf->add('doap:developer', $njh); $easyrdf->add('foaf:maker', $njh); print $doap->serialise('rdfxml'); easyrdf-1.0.0/lib/000077500000000000000000000000001370344162000137135ustar00rootroot00000000000000easyrdf-1.0.0/lib/Collection.php000066400000000000000000000255161370344162000165300ustar00rootroot00000000000000position = 1; $this->current = null; parent::__construct($uri, $graph); } /** Seek to a specific position in the container * * The first item is postion 1 * * @param integer $position The position in the container to seek to * * @throws \OutOfBoundsException * @throws \InvalidArgumentException */ public function seek($position) { if (is_int($position) and $position > 0) { list($node, $actual) = $this->getCollectionNode($position); if ($actual === $position) { $this->position = $actual; $this->current = $node; } else { throw new \OutOfBoundsException( "Unable to seek to position $position in the collection" ); } } else { throw new \InvalidArgumentException( "Collection position must be a positive integer" ); } } /** Rewind the iterator back to the start of the collection * */ public function rewind() { $this->position = 1; $this->current = null; } /** Return the current item in the collection * * @return mixed The current item */ public function current() { if ($this->position === 1) { return $this->get('rdf:first'); } elseif ($this->current) { return $this->current->get('rdf:first'); } } /** Return the key / current position in the collection * * Note: the first item is number 1 * * @return int The current position */ public function key() { return $this->position; } /** Move forward to next item in the collection * */ public function next() { if ($this->position === 1) { $this->current = $this->get('rdf:rest'); } elseif ($this->current) { $this->current = $this->current->get('rdf:rest'); } $this->position++; } /** Checks if current position is valid * * @return bool True if the current position is valid */ public function valid() { if ($this->position === 1 and $this->hasProperty('rdf:first')) { return true; } elseif ($this->current !== null and $this->current->hasProperty('rdf:first')) { return true; } else { return false; } } /** Get a node for a particular offset into the collection * * This function may not return the item you requested, if * it does not exist. Please check the $postion parameter * returned. * * If the offset is null, then the last node in the * collection (before rdf:nil) will be returned. * * @param integer $offset The offset into the collection (or null) * * @return array $node, $postion The node object and postion of the node */ public function getCollectionNode($offset) { $position = 1; $node = $this; $nil = $this->graph->resource('rdf:nil'); while (($rest = $node->get('rdf:rest')) and $rest !== $nil and (is_null($offset) or ($position < $offset))) { $node = $rest; $position++; } return array($node, $position); } /** Counts the number of items in the collection * * Note that this is an slow method - it is more efficient to use * the iterator interface, if you can. * * @return integer The number of items in the collection */ public function count() { // Find the end of the collection list($node, $position) = $this->getCollectionNode(null); if (!$node->hasProperty('rdf:first')) { return 0; } else { return $position; } } /** Append an item to the end of the collection * * @param mixed $value The value to append * * @return integer The number of values appended (1 or 0) */ public function append($value) { // Find the end of the collection list($node, ) = $this->getCollectionNode(null); $rest = $node->get('rdf:rest'); if ($node === $this and is_null($rest)) { $node->set('rdf:first', $value); $node->addResource('rdf:rest', 'rdf:nil'); } else { $new = $this->graph->newBnode(); $node->set('rdf:rest', $new); $new->add('rdf:first', $value); $new->addResource('rdf:rest', 'rdf:nil'); } return 1; } /** Array Access: check if a position exists in collection using array syntax * * Example: isset($list[2]) */ public function offsetExists($offset) { if (is_int($offset) and $offset > 0) { list($node, $position) = $this->getCollectionNode($offset); return ($node and $position === $offset and $node->hasProperty('rdf:first')); } else { throw new \InvalidArgumentException( "Collection offset must be a positive integer" ); } } /** Array Access: get an item at a specified position in collection using array syntax * * Example: $item = $list[2]; */ public function offsetGet($offset) { if (is_int($offset) and $offset > 0) { list($node, $position) = $this->getCollectionNode($offset); if ($node and $position === $offset) { return $node->get('rdf:first'); } } else { throw new \InvalidArgumentException( "Collection offset must be a positive integer" ); } } /** * Array Access: set an item at a positon in collection using array syntax * * Example: $list[2] = $item; */ public function offsetSet($offset, $value) { if (is_null($offset)) { // No offset - append to end of collection $this->append($value); } elseif (is_int($offset) and $offset > 0) { list($node, $position) = $this->getCollectionNode($offset); // Create nodes, if they are missing while ($position < $offset) { $new = $this->graph->newBnode(); $node->set('rdf:rest', $new); $new->addResource('rdf:rest', 'rdf:nil'); $node = $new; $position++; } // Terminate the list if (!$node->hasProperty('rdf:rest')) { $node->addResource('rdf:rest', 'rdf:nil'); } return $node->set('rdf:first', $value); } else { throw new \InvalidArgumentException( "Collection offset must be a positive integer" ); } } /** * Array Access: delete an item at a specific postion using array syntax * * Example: unset($seq[2]); */ public function offsetUnset($offset) { if (is_int($offset) and $offset > 0) { list($node, $position) = $this->getCollectionNode($offset); } else { throw new \InvalidArgumentException( "Collection offset must be a positive integer" ); } // Does the item exist? if ($node and $position === $offset) { $nil = $this->graph->resource('rdf:nil'); if ($position === 1) { $rest = $node->get('rdf:rest'); if ($rest and $rest !== $nil) { // Move second value, so we can keep the head of list $node->set('rdf:first', $rest->get('rdf:first')); $node->set('rdf:rest', $rest->get('rdf:rest')); $rest->delete('rdf:first'); $rest->delete('rdf:rest'); } else { // Just remove the value $node->delete('rdf:first'); $node->delete('rdf:rest'); } } else { // Remove the value and re-link the list $node->delete('rdf:first'); $rest = $node->get('rdf:rest'); $previous = $node->get('^rdf:rest'); if (is_null($rest)) { $rest = $nil; } if ($previous) { $previous->set('rdf:rest', $rest); } } } } } easyrdf-1.0.0/lib/Container.php000066400000000000000000000156321370344162000163550ustar00rootroot00000000000000position = 1; parent::__construct($uri, $graph); } /** Seek to a specific position in the container * * The first item is postion 1 * * @param integer $position The position in the container to seek to * * @throws \OutOfBoundsException * @throws \InvalidArgumentException */ public function seek($position) { if (is_int($position) and $position > 0) { if ($this->hasProperty('rdf:_'.$position)) { $this->position = $position; } else { throw new \OutOfBoundsException( "Unable to seek to position $position in the container" ); } } else { throw new \InvalidArgumentException( "Container position must be a positive integer" ); } } /** Rewind the iterator back to the start of the container (item 1) * */ public function rewind() { $this->position = 1; } /** Return the current item in the container * * @return mixed The current item */ public function current() { return $this->get('rdf:_'.$this->position); } /** Return the key / current position in the container * * @return int The current position */ public function key() { return $this->position; } /** Move forward to next item in the container * */ public function next() { $this->position++; } /** Checks if current position is valid * * @return bool True if the current position is valid */ public function valid() { return $this->hasProperty('rdf:_'.$this->position); } /** Counts the number of items in the container * * Note that this is an slow method - it is more efficient to use * the iterator interface, if you can. * * @return integer The number of items in the container */ public function count() { $pos = 1; while ($this->hasProperty('rdf:_'.$pos)) { $pos++; } return $pos - 1; } /** Append an item to the end of the container * * @param mixed $value The value to append * * @return integer The number of values appended (1 or 0) */ public function append($value) { // Find the end of the list $pos = 1; while ($this->hasProperty('rdf:_'.$pos)) { $pos++; } // Add the item return $this->add('rdf:_'.$pos, $value); } /** Array Access: check if a position exists in container using array syntax * * Example: isset($seq[2]) */ public function offsetExists($offset) { if (is_int($offset) and $offset > 0) { return $this->hasProperty('rdf:_'.$offset); } else { throw new \InvalidArgumentException( "Container position must be a positive integer" ); } } /** Array Access: get an item at a specified position in container using array syntax * * Example: $item = $seq[2]; */ public function offsetGet($offset) { if (is_int($offset) and $offset > 0) { return $this->get('rdf:_'.$offset); } else { throw new \InvalidArgumentException( "Container position must be a positive integer" ); } } /** * Array Access: set an item at a positon in container using array syntax * * Example: $seq[2] = $item; * * Warning: creating gaps in the sequence will result in unexpected behavior */ public function offsetSet($offset, $value) { if (is_int($offset) and $offset > 0) { return $this->set('rdf:_'.$offset, $value); } elseif (is_null($offset)) { return $this->append($value); } else { throw new \InvalidArgumentException( "Container position must be a positive integer" ); } } /** * Array Access: delete an item at a specific postion using array syntax * * Example: unset($seq[2]); * * Warning: creating gaps in the sequence will result in unexpected behavior */ public function offsetUnset($offset) { if (is_int($offset) and $offset > 0) { return $this->delete('rdf:_'.$offset); } else { throw new \InvalidArgumentException( "Container position must be a positive integer" ); } } } easyrdf-1.0.0/lib/Exception.php000066400000000000000000000040631370344162000163650ustar00rootroot00000000000000 0.5) where 0.5 is the * q value for that type. The types are sorted by q value * before constructing the string. * * @param array $extraTypes extra MIME types to add * * @return string list of supported MIME types */ public static function getHttpAcceptHeader(array $extraTypes = array()) { $accept = $extraTypes; foreach (self::$formats as $format) { if ($format->parserClass and count($format->mimeTypes) > 0) { $accept = array_merge($accept, $format->mimeTypes); } } return self::formatAcceptHeader($accept); } /** * Convert array of types to Accept header value * @param array $accepted_types * @return string */ public static function formatAcceptHeader(array $accepted_types) { arsort($accepted_types, SORT_NUMERIC); $acceptStr = ''; foreach ($accepted_types as $type => $q) { if ($acceptStr) { $acceptStr .= ','; } if ($q == 1.0) { $acceptStr .= $type; } else { $acceptStr .= sprintf("%s;q=%1.1F", $type, $q); } } return $acceptStr; } /** Check if a named graph exists * * @param string $name the name of the format * * @return boolean true if the format exists */ public static function formatExists($name) { return array_key_exists($name, self::$formats); } /** Get a EasyRdf\Format from a name, uri or mime type * * @param string $query a query string to search for * * @return self the first object that matches the query * @throws \InvalidArgumentException * @throws Exception if no format is found */ public static function getFormat($query) { if (!is_string($query) or $query == null or $query == '') { throw new \InvalidArgumentException( "\$query should be a string and cannot be null or empty" ); } foreach (self::$formats as $format) { if ($query == $format->name or $query == $format->uri or array_key_exists($query, $format->mimeTypes) or in_array($query, $format->extensions)) { return $format; } } # No match throw new Exception( "Format is not recognised: $query" ); } /** Register a new format * * @param string $name The name of the format (e.g. ntriples) * @param string $label The label for the format (e.g. N-Triples) * @param string $uri The URI for the format * @param array|string $mimeTypes One or more mime types for the format * @param array|string $extensions One or more extensions (file suffix) * * @throws \InvalidArgumentException * @return self New Format object */ public static function register( $name, $label = null, $uri = null, $mimeTypes = array(), $extensions = array() ) { if (!is_string($name) or $name == null or $name == '') { throw new \InvalidArgumentException( "\$name should be a string and cannot be null or empty" ); } if (!array_key_exists($name, self::$formats)) { self::$formats[$name] = new self($name); } self::$formats[$name]->setLabel($label); self::$formats[$name]->setUri($uri); self::$formats[$name]->setMimeTypes($mimeTypes); self::$formats[$name]->setExtensions($extensions); return self::$formats[$name]; } /** Remove a format from the registry * * @param string $name The name of the format (e.g. ntriples) */ public static function unregister($name) { unset(self::$formats[$name]); } /** Class method to register a parser class to a format name * * @param string $name The name of the format (e.g. ntriples) * @param string $class The name of the class (e.g. EasyRdf\Parser\Ntriples) */ public static function registerParser($name, $class) { if (!self::formatExists($name)) { self::register($name); } self::getFormat($name)->setParserClass($class); } /** Class method to register a serialiser class to a format name * * @param string $name The name of the format (e.g. ntriples) * @param string $class The name of the class (e.g. EasyRdf\Serialiser\Ntriples) */ public static function registerSerialiser($name, $class) { if (!self::formatExists($name)) { self::register($name); } self::getFormat($name)->setSerialiserClass($class); } /** Attempt to guess the document format from some content. * * If $filename is given, then the suffix is first used to guess the format. * * If the document format is not recognised, null is returned. * * @param string $data The document data * @param string $filename Optional filename * * @return self New format object */ public static function guessFormat($data, $filename = null) { if (is_array($data)) { # Data has already been parsed into RDF/PHP return self::getFormat('php'); } // First try and identify by the filename if ($filename and preg_match('/\.(\w+)$/', $filename, $matches)) { foreach (self::$formats as $format) { if (in_array($matches[1], $format->extensions)) { return $format; } } } // Then try and guess by the first 1024 bytes of content $short = substr($data, 0, 1024); if (preg_match('/^\s*\{/', $short)) { return self::getFormat('json'); } elseif (preg_match('/ <.+>/m', $short)) { return self::getFormat('ntriples'); } else { return null; } } /** * This constructor is for internal use only. * To create a new format, use the register method. * * @param string $name The name of the format * @see Format::register() * @ignore */ public function __construct($name) { $this->name = $name; $this->label = $name; # Only a default } /** Get the name of a format object * * @return string The name of the format (e.g. rdfxml) */ public function getName() { return $this->name; } /** Get the label for a format object * * @return string The format label (e.g. RDF/XML) */ public function getLabel() { return $this->label; } /** Set the label for a format object * * @param string $label The new label for the format * * @throws \InvalidArgumentException * @return string|null */ public function setLabel($label) { if ($label) { if (!is_string($label)) { throw new \InvalidArgumentException( "\$label should be a string" ); } return $this->label = $label; } else { return $this->label = null; } } /** Get the URI for a format object * * @return string The format URI */ public function getUri() { return $this->uri; } /** Set the URI for a format object * * @param string $uri The new URI for the format * * @throws \InvalidArgumentException * @return string|null */ public function setUri($uri) { if ($uri) { if (!is_string($uri)) { throw new \InvalidArgumentException( "\$uri should be a string" ); } return $this->uri = $uri; } else { return $this->uri = null; } } /** Get the default registered mime type for a format object * * @return string The default mime type as a string. */ public function getDefaultMimeType() { $types = array_keys($this->mimeTypes); if (isset($types[0])) { return $types[0]; } } /** Get all the registered mime types for a format object * * @return array One or more MIME types in an array with * the mime type as the key and q value as the value */ public function getMimeTypes() { return $this->mimeTypes; } /** Set the MIME Types for a format object * * @param string|array $mimeTypes One or more mime types */ public function setMimeTypes($mimeTypes) { if ($mimeTypes) { if (!is_array($mimeTypes)) { $mimeTypes = array($mimeTypes); } $this->mimeTypes = $mimeTypes; } else { $this->mimeTypes = array(); } } /** Get the default registered file extension (filename suffix) for a format object * * @return string The default extension as a string. */ public function getDefaultExtension() { if (isset($this->extensions[0])) { return $this->extensions[0]; } } /** Get all the registered file extensions (filename suffix) for a format object * * @return array One or more extensions as an array */ public function getExtensions() { return $this->extensions; } /** Set the file format extensions (filename suffix) for a format object * * @param mixed $extensions One or more file extensions */ public function setExtensions($extensions) { if ($extensions) { if (!is_array($extensions)) { $extensions = array($extensions); } $this->extensions = $extensions; } else { $this->extensions = array(); } } /** Set the parser to use for a format * * @param string $class The name of the class * * @throws \InvalidArgumentException */ public function setParserClass($class) { if ($class) { if (!is_string($class)) { throw new \InvalidArgumentException( "\$class should be a string" ); } $this->parserClass = $class; } else { $this->parserClass = null; } } /** Get the name of the class to use to parse the format * * @return string The name of the class */ public function getParserClass() { return $this->parserClass; } /** Create a new parser to parse this format * * @throws Exception * @return object The new parser object */ public function newParser() { $parserClass = $this->parserClass; if (!$parserClass) { throw new Exception( "No parser class available for format: ".$this->getName() ); } return (new $parserClass()); } /** Set the serialiser to use for a format * * @param string $class The name of the class * * @throws \InvalidArgumentException */ public function setSerialiserClass($class) { if ($class) { if (!is_string($class)) { throw new \InvalidArgumentException( "\$class should be a string" ); } $this->serialiserClass = $class; } else { $this->serialiserClass = null; } } /** Get the name of the class to use to serialise the format * * @return string The name of the class */ public function getSerialiserClass() { return $this->serialiserClass; } /** Create a new serialiser to parse this format * * @throws Exception * @return object The new serialiser object */ public function newSerialiser() { $serialiserClass = $this->serialiserClass; if (!$serialiserClass) { throw new Exception( "No serialiser class available for format: ".$this->getName() ); } return (new $serialiserClass()); } /** Magic method to return the name of the format when casted to string * * @return string The name of the format */ public function __toString() { return $this->name; } } /* Register default set of supported formats NOTE: they are ordered by preference */ Format::register( 'php', 'RDF/PHP', 'https://www.easyrdf.org/docs/rdf-formats-php', array( 'application/x-httpd-php-source' => 1.0 ), array('phps') ); Format::register( 'json', 'RDF/JSON Resource-Centric', 'https://www.easyrdf.org/docs/rdf-formats-json', array( 'application/json' => 1.0, 'text/json' => 0.9, 'application/rdf+json' => 0.9 ), array('json') ); Format::register( 'jsonld', 'JSON-LD', 'http://www.w3.org/TR/json-ld/', array( 'application/ld+json' => 1.0 ), array('jsonld') ); Format::register( 'ntriples', 'N-Triples', 'http://www.w3.org/TR/n-triples/', array( 'application/n-triples' => 1.0, 'text/plain' => 0.9, 'text/ntriples' => 0.9, 'application/ntriples' => 0.9, 'application/x-ntriples' => 0.9 ), array('nt') ); Format::register( 'turtle', 'Turtle Terse RDF Triple Language', 'https://www.w3.org/TR/turtle/', array( 'text/turtle' => 0.8, 'application/turtle' => 0.7, 'application/x-turtle' => 0.7 ), array('ttl') ); Format::register( 'rdfxml', 'RDF/XML', 'http://www.w3.org/TR/rdf-syntax-grammar/', array( 'application/rdf+xml' => 0.8, 'text/xml' => 0.5, 'application/xml' => 0.5 ), array('rdf', 'xrdf') ); Format::register( 'dot', 'Graphviz', 'http://www.graphviz.org/doc/info/lang.html', array( 'text/vnd.graphviz' => 0.8 ), array('gv', 'dot') ); Format::register( 'json-triples', 'RDF/JSON Triples' ); Format::register( 'n3', 'Notation3', 'http://www.w3.org/2000/10/swap/grammar/n3#', array( 'text/n3' => 0.5, 'text/rdf+n3' => 0.5 ), array('n3') ); Format::register( 'rdfa', 'RDFa', 'http://www.w3.org/TR/rdfa-core/', array( 'text/html' => 0.4, 'application/xhtml+xml' => 0.4 ), array('html') ); Format::register( 'sparql-xml', 'SPARQL XML Query Results', 'http://www.w3.org/TR/rdf-sparql-XMLres/', array( 'application/sparql-results+xml' => 1.0 ) ); Format::register( 'sparql-json', 'SPARQL JSON Query Results', 'http://www.w3.org/TR/rdf-sparql-json-res/', array( 'application/sparql-results+json' => 1.0 ) ); Format::register( 'png', 'Portable Network Graphics (PNG)', 'http://www.w3.org/TR/PNG/', array( 'image/png' => 0.3 ), array('png') ); Format::register( 'gif', 'Graphics Interchange Format (GIF)', 'http://www.w3.org/Graphics/GIF/spec-gif89a.txt', array( 'image/gif' => 0.2 ), array('gif') ); Format::register( 'svg', 'Scalable Vector Graphics (SVG)', 'http://www.w3.org/TR/SVG/', array( 'image/svg+xml' => 0.3 ), array('svg') ); /* Register default set of parsers and serialisers */ Format::registerParser('json', 'EasyRdf\Parser\Json'); Format::registerParser('jsonld', 'EasyRdf\Parser\JsonLd'); Format::registerParser('ntriples', 'EasyRdf\Parser\Ntriples'); Format::registerParser('php', 'EasyRdf\Parser\RdfPhp'); Format::registerParser('rdfxml', 'EasyRdf\Parser\RdfXml'); Format::registerParser('turtle', 'EasyRdf\Parser\Turtle'); Format::registerParser('rdfa', 'EasyRdf\Parser\Rdfa'); Format::registerSerialiser('json', 'EasyRdf\Serialiser\Json'); Format::registerSerialiser('jsonld', 'EasyRdf\Serialiser\JsonLd'); Format::registerSerialiser('n3', 'EasyRdf\Serialiser\Turtle'); Format::registerSerialiser('ntriples', 'EasyRdf\Serialiser\Ntriples'); Format::registerSerialiser('php', 'EasyRdf\Serialiser\RdfPhp'); Format::registerSerialiser('rdfxml', 'EasyRdf\Serialiser\RdfXml'); Format::registerSerialiser('turtle', 'EasyRdf\Serialiser\Turtle'); Format::registerSerialiser('dot', 'EasyRdf\Serialiser\GraphViz'); Format::registerSerialiser('gif', 'EasyRdf\Serialiser\GraphViz'); Format::registerSerialiser('png', 'EasyRdf\Serialiser\GraphViz'); Format::registerSerialiser('svg', 'EasyRdf\Serialiser\GraphViz'); easyrdf-1.0.0/lib/Graph.php000066400000000000000000001653371370344162000155040ustar00rootroot00000000000000checkResourceParam($uri, true); if ($uri) { $this->uri = $uri; $this->parsedUri = new ParsedUri($uri); if ($data) { $this->parse($data, $format, $this->uri); } } } /** * Create a new graph and load RDF data from a URI into it * * This static function is shorthand for: * $graph = new \EasyRdf\Graph($uri); * $graph->load($uri, $format); * * The document format is optional but should be specified if it * can't be guessed or got from the HTTP headers. * * If the document format is given, then the HTTP Accept header is * set to the MIME type of the requested format. * * @param string $uri The URI of the data to load * @param string|null $format Optional format of the data (eg. rdfxml or text/turtle) * * @return Graph The new the graph object */ public static function newAndLoad($uri, $format = null) { $graph = new self($uri); $graph->load($uri, $format); return $graph; } /** Get or create a resource stored in a graph * * If the resource did not previously exist, then a new resource will * be created. If you provide an RDF type and that type is registered * with the EasyRdf\TypeMapper, then the resource will be an instance * of the class registered. * * If URI is null, then the URI of the graph is used. * * @param string $uri The URI of the resource * @param mixed $types RDF type of a new resource (e.g. foaf:Person) * * @throws \InvalidArgumentException * @return \EasyRdf\Resource */ public function resource($uri = null, $types = array()) { $this->checkResourceParam($uri, true); if (!$uri) { throw new \InvalidArgumentException( '$uri is null and EasyRdf\Graph object has no URI either.' ); } // Resolve relative URIs if ($this->parsedUri) { $uri = $this->parsedUri->resolve($uri)->toString(); } // Add the types $this->addType($uri, $types); // Create resource object if it doesn't already exist if (!isset($this->resources[$uri])) { $resClass = $this->classForResource($uri); $this->resources[$uri] = new $resClass($uri, $this); } return $this->resources[$uri]; } /** Work out the class to instantiate a resource as * @ignore */ protected function classForResource($uri) { $rdfType = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; if (isset($this->index[$uri][$rdfType])) { foreach ($this->index[$uri][$rdfType] as $type) { if ($type['type'] == 'uri' or $type['type'] == 'bnode') { $class = TypeMapper::get($type['value']); if ($class != null) { return $class; } } } } // Parsers don't typically add a rdf:type to rdf:List, so we have to // do a bit of 'inference' here using properties. if ($uri == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil' or isset($this->index[$uri]['http://www.w3.org/1999/02/22-rdf-syntax-ns#first']) or isset($this->index[$uri]['http://www.w3.org/1999/02/22-rdf-syntax-ns#rest']) ) { return 'EasyRdf\Collection'; } return TypeMapper::getDefaultResourceClass(); } /** * Create a new blank node in the graph and return it. * * If you provide an RDF type and that type is registered * with the EasyRdf\TypeMapper, then the resource will be an instance * of the class registered. * * @param mixed $types RDF type of a new blank node (e.g. foaf:Person) * * @return \EasyRdf\Resource The new blank node */ public function newBNode($types = array()) { return $this->resource($this->newBNodeId(), $types); } /** * Create a new unique blank node identifier and return it. * * @return string The new blank node identifier (e.g. _:genid1) */ public function newBNodeId() { return "_:genid".(++$this->bNodeCount); } /** * Parse some RDF data into the graph object. * * @param string $data Data to parse for the graph * @param string $format Optional format of the data * @param string $uri The URI of the data to load * * @throws Exception * @return integer The number of triples added to the graph */ public function parse($data, $format = null, $uri = null) { $this->checkResourceParam($uri, true); if (empty($format) or $format == 'guess') { // Guess the format if it is Unknown $format = Format::guessFormat($data, $uri); } else { $format = Format::getFormat($format); } if (!$format) { throw new Exception( "Unable to parse data of an unknown format." ); } $parser = $format->newParser(); return $parser->parse($this, $data, $format, $uri); } /** * Parse a file containing RDF data into the graph object. * * @param string $filename The path of the file to load * @param string $format Optional format of the file * @param string $uri The URI of the file to load * * @return integer The number of triples added to the graph */ public function parseFile($filename, $format = null, $uri = null) { if ($uri === null) { $uri = "file://$filename"; } return $this->parse( file_get_contents($filename), $format, $uri ); } /** * Load RDF data into the graph from a URI. * * If no URI is given, then the URI of the graph will be used. * * The document format is optional but should be specified if it * can't be guessed or got from the HTTP headers. * * If the document format is given, then the HTTP Accept header is * set to the MIME type of the requested format. * * @param string $uri The URI of the data to load * @param string $format Optional format of the data (eg. rdfxml or text/turtle) * * @throws Exception * @throws Http\Exception * @return integer The number of triples added to the graph */ public function load($uri = null, $format = null) { $this->checkResourceParam($uri, true); if (!$uri) { throw new Exception( "No URI given to load() and the graph does not have a URI." ); } // Setup the HTTP client $client = Http::getDefaultHttpClient(); $client->resetParameters(true); $client->setConfig(array('maxredirects' => 0)); $client->setMethod('GET'); if ($format && $format !== 'guess') { if (strpos($format, '/') !== false) { $client->setHeaders('Accept', $format); } else { $formatObj = Format::getFormat($format); $client->setHeaders('Accept', $formatObj->getDefaultMimeType()); } } else { // Send a list of all the formats we can parse $client->setHeaders('Accept', Format::getHttpAcceptHeader()); } $requestUrl = $uri; $response = null; $redirectCounter = 0; do { // Have we already loaded it into the graph? $requestUrl = Utils::removeFragmentFromUri($requestUrl); if (in_array($requestUrl, $this->loaded)) { return 0; } // Make the HTTP request $client->setHeaders('host', null); $client->setUri($requestUrl); $response = $client->request(); // Add the URL to the list of URLs loaded $this->loaded[] = $requestUrl; if ($response->isRedirect() and $location = $response->getHeader('location')) { // Avoid problems with buggy servers that add whitespace $location = trim($location); // Some servers return relative URLs in the location header // resolve it in relation to previous request $baseUri = new ParsedUri($requestUrl); $requestUrl = $baseUri->resolve($location)->toString(); $requestUrl = Utils::removeFragmentFromUri($requestUrl); // If it is a 303 then drop the parameters if ($response->getStatus() == 303) { $client->resetParameters(); } ++$redirectCounter; } elseif ($response->isSuccessful()) { // If we didn't get any location, stop redirecting break; } else { throw new Http\Exception( "HTTP request for {$requestUrl} failed: ".$response->getMessage(), $response->getStatus(), null, $response->getBody() ); } } while ($redirectCounter < $this->maxRedirects); if (!$format or $format == 'guess') { list($format, ) = Utils::parseMimeType( $response->getHeader('Content-Type') ); } // Parse the data return $this->parse($response->getBody(), $format, $uri); } /** Get an associative array of all the resources stored in the graph. * The keys of the array is the URI of the EasyRdf\Resource. * * @return Resource[] */ public function resources() { foreach ($this->index as $subject => $properties) { if (!isset($this->resources[$subject])) { $this->resource($subject); } } foreach ($this->revIndex as $object => $properties) { if (!isset($this->resources[$object])) { $this->resource($object); } } return $this->resources; } /** Get an arry of resources matching a certain property and optional value. * * For example this routine could be used as a way of getting * everyone who has name: * $people = $graph->resourcesMatching('foaf:name') * * Or everyone who is male: * $people = $graph->resourcesMatching('foaf:gender', 'male'); * * Or all homepages: * $people = $graph->resourcesMatching('^foaf:homepage'); * * @param string $property The property to check. * @param mixed $value Optional, the value of the propery to check for. * * @return Resource[] */ public function resourcesMatching($property, $value = null) { $this->checkSinglePropertyParam($property, $inverse); $this->checkValueParam($value); // Use the reverse index if it is an inverse property if ($inverse) { $index = &$this->revIndex; } else { $index = &$this->index; } $matched = array(); foreach ($index as $subject => $props) { if (isset($index[$subject][$property])) { if (isset($value)) { foreach ($this->index[$subject][$property] as $v) { if ($v['type'] == $value['type'] and $v['value'] == $value['value']) { $matched[] = $this->resource($subject); break; } } } else { $matched[] = $this->resource($subject); } } } return $matched; } /** Get the URI of the graph * * @return string The URI of the graph */ public function getUri() { return $this->uri; } /** Check that a URI/resource parameter is valid, and convert it to a string * @ignore */ protected function checkResourceParam(&$resource, $allowNull = false) { if ($allowNull == true) { if ($resource === null) { if ($this->uri) { $resource = $this->uri; } else { return; } } } elseif ($resource === null) { throw new \InvalidArgumentException( '$resource should be either IRI, blank-node identifier or EasyRdf\Resource. got null' ); } if (is_object($resource) and $resource instanceof Resource) { $resource = $resource->getUri(); } elseif (is_object($resource) and $resource instanceof ParsedUri) { $resource = strval($resource); } elseif (is_string($resource)) { if ($resource == '') { throw new \InvalidArgumentException( '$resource should be either IRI, blank-node identifier or EasyRdf\Resource. got empty string' ); } elseif (preg_match("|^<(.+)>$|", $resource, $matches)) { $resource = $matches[1]; } else { $resource = RdfNamespace::expand($resource); } } else { throw new \InvalidArgumentException( '$resource should be either IRI, blank-node identifier or EasyRdf\Resource' ); } } /** Check that a single URI/property parameter (not a property path) * is valid, and expand it if required * @ignore */ protected function checkSinglePropertyParam(&$property, &$inverse) { if (is_object($property) and $property instanceof Resource) { $property = $property->getUri(); } elseif (is_object($property) and $property instanceof ParsedUri) { $property = strval($property); } elseif (is_string($property)) { if ($property == '') { throw new \InvalidArgumentException( "\$property cannot be an empty string" ); } elseif (substr($property, 0, 1) == '^') { $inverse = true; $property = RdfNamespace::expand(substr($property, 1)); } elseif (substr($property, 0, 2) == '_:') { throw new \InvalidArgumentException( "\$property cannot be a blank node" ); } else { $inverse = false; $property = RdfNamespace::expand($property); } } if ($property === null or !is_string($property)) { throw new \InvalidArgumentException( '$property should be a string or EasyRdf\Resource and cannot be null' ); } } /** Check that a value parameter is valid, and convert it to an associative array if needed * @ignore */ protected function checkValueParam(&$value) { if (isset($value)) { if (is_object($value)) { if (!method_exists($value, 'toRdfPhp')) { // Convert to a literal object $value = Literal::create($value); } $value = $value->toRdfPhp(); } elseif (is_array($value)) { if (!isset($value['type'])) { throw new \InvalidArgumentException( "\$value is missing a 'type' key" ); } if (!isset($value['value'])) { throw new \InvalidArgumentException( "\$value is missing a 'value' key" ); } // Fix ordering and remove unknown keys $value = array( 'type' => strval($value['type']), 'value' => strval($value['value']), 'lang' => isset($value['lang']) ? strval($value['lang']) : null, 'datatype' => isset($value['datatype']) ? strval($value['datatype']) : null ); } else { $value = array( 'type' => 'literal', 'value' => strval($value), 'datatype' => Literal::getDatatypeForValue($value) ); } if (!in_array($value['type'], array('uri', 'bnode', 'literal'), true)) { throw new \InvalidArgumentException( "\$value does not have a valid type (".$value['type'].")" ); } if (empty($value['datatype'])) { unset($value['datatype']); } if (empty($value['lang'])) { unset($value['lang']); } if (isset($value['lang']) and isset($value['datatype'])) { throw new \InvalidArgumentException( "\$value cannot have both and language and a datatype" ); } } } /** Get a single value for a property of a resource * * If multiple values are set for a property then the value returned * may be arbitrary. * * If $property is an array, then the first item in the array that matches * a property that exists is returned. * * This method will return null if the property does not exist. * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string $propertyPath A valid property path * @param string $type The type of value to filter by (e.g. literal or resource) * @param string $lang The language to filter by (e.g. en) * * @throws \InvalidArgumentException * @return mixed A value associated with the property */ public function get($resource, $propertyPath, $type = null, $lang = null) { $this->checkResourceParam($resource); if (is_object($propertyPath) and $propertyPath instanceof Resource) { return $this->getSingleProperty($resource, $propertyPath->getUri(), $type, $lang); } elseif (is_string($propertyPath) and preg_match('|^(\^?)<(.+)>|', $propertyPath, $matches)) { return $this->getSingleProperty($resource, "$matches[1]$matches[2]", $type, $lang); } elseif ($propertyPath === null or !is_string($propertyPath)) { throw new \InvalidArgumentException( '$propertyPath should be a string or EasyRdf\Resource and cannot be null' ); } elseif ($propertyPath === '') { throw new \InvalidArgumentException( "\$propertyPath cannot be an empty string" ); } // Loop through each component in the path foreach (explode('/', $propertyPath) as $part) { // Stop if we come to a literal if ($resource instanceof Literal) { return null; } // Try each of the alternative paths foreach (explode('|', $part) as $p) { $res = $this->getSingleProperty($resource, $p, $type, $lang); if ($res) { break; } } // Stop if nothing was found $resource = $res; if (!$resource) { break; } } return $resource; } /** Get a single value for a property of a resource * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string $property The name of the property (e.g. foaf:name) * @param string $type The type of value to filter by (e.g. literal or resource) * @param string $lang The language to filter by (e.g. en) * * @return mixed A value associated with the property * * @ignore */ protected function getSingleProperty($resource, $property, $type = null, $lang = null) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); // Get an array of values for the property $values = $this->propertyValuesArray($resource, $property, $inverse); if (!isset($values)) { return null; } // Filter the results $result = null; if ($type) { foreach ($values as $value) { if ($type == 'literal' and $value['type'] == 'literal') { if ($lang == null or (isset($value['lang']) and $value['lang'] == $lang)) { $result = $value; break; } } elseif ($type == 'resource') { if ($value['type'] == 'uri' or $value['type'] == 'bnode') { $result = $value; break; } } } } else { $result = $values[0]; } // Convert the internal data structure into a PHP object return $this->arrayToObject($result); } /** Get a single literal value for a property of a resource * * If multiple values are set for a property then the value returned * may be arbitrary. * * This method will return null if there is not literal value for the * property. * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string|array $property The name of the property (e.g. foaf:name) * @param string $lang The language to filter by (e.g. en) * * @return Literal Literal value associated with the property */ public function getLiteral($resource, $property, $lang = null) { return $this->get($resource, $property, 'literal', $lang); } /** Get a single resource value for a property of a resource * * If multiple values are set for a property then the value returned * may be arbitrary. * * This method will return null if there is not resource for the * property. * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string|array $property The name of the property (e.g. foaf:name) * * @return \EasyRdf\Resource Resource associated with the property */ public function getResource($resource, $property) { return $this->get($resource, $property, 'resource'); } /** Return all the values for a particular property of a resource * @ignore */ protected function propertyValuesArray($resource, $property, $inverse = false) { // Is an inverse property being requested? if ($inverse) { if (isset($this->revIndex[$resource])) { $properties = &$this->revIndex[$resource]; } } else { if (isset($this->index[$resource])) { $properties = &$this->index[$resource]; } } if (isset($properties[$property])) { return $properties[$property]; } else { return null; } } /** Get an EasyRdf\Resource or EasyRdf\Literal object from an associative array. * @ignore */ protected function arrayToObject($data) { if ($data) { if ($data['type'] == 'uri' or $data['type'] == 'bnode') { return $this->resource($data['value']); } else { return Literal::create($data); } } else { return null; } } /** Get all values for a property path * * This method will return an empty array if the property does not exist. * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string $propertyPath A valid property path * @param string $type The type of value to filter by (e.g. literal) * @param string $lang The language to filter by (e.g. en) * * @throws \InvalidArgumentException * @return array An array of values associated with the property */ public function all($resource, $propertyPath, $type = null, $lang = null) { $this->checkResourceParam($resource); if (is_object($propertyPath) and $propertyPath instanceof Resource) { return $this->allForSingleProperty($resource, $propertyPath->getUri(), $type, $lang); } elseif (is_string($propertyPath) and preg_match('|^(\^?)<(.+)>|', $propertyPath, $matches)) { return $this->allForSingleProperty($resource, "$matches[1]$matches[2]", $type, $lang); } elseif ($propertyPath === null or !is_string($propertyPath)) { throw new \InvalidArgumentException( '$propertyPath should be a string or EasyRdf\Resource and cannot be null' ); } elseif ($propertyPath === '') { throw new \InvalidArgumentException( "\$propertyPath cannot be an empty string" ); } $objects = array($resource); // Loop through each component in the path foreach (explode('/', $propertyPath) as $part) { $results = array(); foreach (explode('|', $part) as $p) { foreach ($objects as $o) { // Ignore literals found earlier in path if ($o instanceof Literal) { continue; } $results = array_merge( $results, $this->allForSingleProperty($o, $p, $type, $lang) ); } } // Stop if we don't have anything if (empty($objects)) { break; } // Use the results as the input to the next iteration $objects = $results; } return $results; } /** Get all values for a single property of a resource * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string $property The name of the property (e.g. foaf:name) * @param string $type The type of value to filter by (e.g. literal) * @param string $lang The language to filter by (e.g. en) * * @return array An array of values associated with the property * * @ignore */ protected function allForSingleProperty($resource, $property, $type = null, $lang = null) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); // Get an array of values for the property $values = $this->propertyValuesArray($resource, $property, $inverse); if (!isset($values)) { return array(); } $objects = array(); if ($type) { foreach ($values as $value) { if ($type == 'literal' and $value['type'] == 'literal') { if ($lang == null or (isset($value['lang']) and $value['lang'] == $lang)) { $objects[] = $this->arrayToObject($value); } } elseif ($type == 'resource') { if ($value['type'] == 'uri' or $value['type'] == 'bnode') { $objects[] = $this->arrayToObject($value); } } } } else { foreach ($values as $value) { $objects[] = $this->arrayToObject($value); } } return $objects; } /** Get all literal values for a property of a resource * * This method will return an empty array if the resource does not * has any literal values for that property. * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string $property The name of the property (e.g. foaf:name) * @param string $lang The language to filter by (e.g. en) * * @return array An array of values associated with the property */ public function allLiterals($resource, $property, $lang = null) { return $this->all($resource, $property, 'literal', $lang); } /** Get all resources for a property of a resource * * This method will return an empty array if the resource does not * has any resources for that property. * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string $property The name of the property (e.g. foaf:name) * * @return array An array of values associated with the property */ public function allResources($resource, $property) { return $this->all($resource, $property, 'resource'); } /** Get all the resources in the graph of a certain type * * If no resources of the type are available and empty * array is returned. * * @param string $type The type of the resource (e.g. foaf:Person) * * @return array The array of resources */ public function allOfType($type) { return $this->all($type, '^rdf:type'); } /** Count the number of values for a property of a resource * * @param string $resource The URI of the resource (e.g. http://example.com/joe#me) * @param string $property The name of the property (e.g. foaf:name) * @param string $type The type of value to filter by (e.g. literal) * @param string $lang The language to filter by (e.g. en) * * @return integer The number of values for this property */ public function countValues($resource, $property, $type = null, $lang = null) { return count($this->all($resource, $property, $type, $lang)); } /** Concatenate all values for a property of a resource into a string. * * The default is to join the values together with a space character. * This method will return an empty string if the property does not exist. * * @param mixed $resource The resource to get the property on * @param string $property The name of the property (e.g. foaf:name) * @param string $glue The string to glue the values together with. * @param string $lang The language to filter by (e.g. en) * * @return string Concatenation of all the values. */ public function join($resource, $property, $glue = ' ', $lang = null) { return join($glue, $this->all($resource, $property, 'literal', $lang)); } /** Add data to the graph * * The resource can either be a resource or the URI of a resource. * * Example: * $graph->add("http://www.example.com", 'dc:title', 'Title of Page'); * * @param mixed $resource The resource to add data to * @param mixed $property The property name * @param mixed $value The new value for the property * * @return integer The number of values added (1 or 0) */ public function add($resource, $property, $value) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); $this->checkValueParam($value); // No value given? if ($value === null) { return 0; } // Check that the value doesn't already exist if (isset($this->index[$resource][$property])) { foreach ($this->index[$resource][$property] as $v) { if ($v == $value) { return 0; } } } $this->index[$resource][$property][] = $value; // Add to the reverse index if it is a resource if ($value['type'] == 'uri' or $value['type'] == 'bnode') { $uri = $value['value']; $this->revIndex[$uri][$property][] = array( 'type' => substr($resource, 0, 2) == '_:' ? 'bnode' : 'uri', 'value' => $resource ); } // Success return 1; } /** Add a literal value as a property of a resource * * The resource can either be a resource or the URI of a resource. * The value can either be a single value or an array of values. * * Example: * $graph->add("http://www.example.com", 'dc:title', 'Title of Page'); * * @param mixed $resource The resource to add data to * @param mixed $property The property name * @param mixed $value The value or values for the property * @param string $lang The language of the literal * * @return integer The number of values added */ public function addLiteral($resource, $property, $value, $lang = null) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); if (is_array($value)) { $added = 0; foreach ($value as $v) { $added += $this->addLiteral($resource, $property, $v, $lang); } return $added; } elseif (!is_object($value) or !$value instanceof Literal) { $value = Literal::create($value, $lang); } return $this->add($resource, $property, $value); } /** Add a resource as a property of another resource * * The resource can either be a resource or the URI of a resource. * * Example: * $graph->add("http://example.com/bob", 'foaf:knows', 'http://example.com/alice'); * * @param mixed $resource The resource to add data to * @param mixed $property The property name * @param mixed $resource2 The resource to be value of the property * * @return integer The number of values added */ public function addResource($resource, $property, $resource2) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); $this->checkResourceParam($resource2); return $this->add( $resource, $property, array( 'type' => substr($resource2, 0, 2) == '_:' ? 'bnode' : 'uri', 'value' => $resource2 ) ); } /** Set a value for a property * * The new value will replace the existing values for the property. * * @param string $resource The resource to set the property on * @param string $property The name of the property (e.g. foaf:name) * @param mixed $value The value for the property * * @return integer The number of values added (1 or 0) */ public function set($resource, $property, $value) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); $this->checkValueParam($value); // Delete the old values $this->delete($resource, $property); // Add the new values return $this->add($resource, $property, $value); } /** Delete a property (or optionally just a specific value) * * @param mixed $resource The resource to delete the property from * @param string $property The name of the property (e.g. foaf:name) * @param mixed $value The value to delete (null to delete all values) * * @throws \InvalidArgumentException * @return integer The number of values deleted */ public function delete($resource, $property, $value = null) { $this->checkResourceParam($resource); if (is_object($property) and $property instanceof Resource) { return $this->deleteSingleProperty($resource, $property->getUri(), $value); } elseif (is_string($property) and preg_match('|^(\^?)<(.+)>|', $property, $matches)) { return $this->deleteSingleProperty($resource, "$matches[1]$matches[2]", $value); } elseif ($property === null or !is_string($property)) { throw new \InvalidArgumentException( '$property should be a string or EasyRdf\Resource and cannot be null' ); } elseif ($property === '') { throw new \InvalidArgumentException( "\$property cannot be an empty string" ); } // FIXME: finish implementing property paths for delete return $this->deleteSingleProperty($resource, $property, $value); } /** Delete a property (or optionally just a specific value) * * @param mixed $resource The resource to delete the property from * @param string $property The name of the property (e.g. foaf:name) * @param mixed $value The value to delete (null to delete all values) * * @return integer The number of values deleted * * @ignore */ public function deleteSingleProperty($resource, $property, $value = null) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); $this->checkValueParam($value); $count = 0; if (isset($this->index[$resource][$property])) { $newValues = array(); foreach ($this->index[$resource][$property] as $k => $v) { if (!$value or $v == $value) { $count++; if ($v['type'] == 'uri' or $v['type'] == 'bnode') { $this->deleteInverse($v['value'], $property, $resource); } } else { $newValues[] = $v; } } // Clean up the indexes - remove empty properties and resources if ($count) { if (count($newValues) == 0) { unset($this->index[$resource][$property]); } else { $this->index[$resource][$property] = $newValues; } if (count($this->index[$resource]) == 0) { unset($this->index[$resource]); } } } return $count; } /** Delete a resource from a property of another resource * * The resource can either be a resource or the URI of a resource. * * Example: * $graph->delete("http://example.com/bob", 'foaf:knows', 'http://example.com/alice'); * * @param mixed $resource The resource to delete data from * @param mixed $property The property name * @param mixed $resource2 The resource value of the property to be deleted * * @return integer */ public function deleteResource($resource, $property, $resource2) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); $this->checkResourceParam($resource2); return $this->delete( $resource, $property, array( 'type' => substr($resource2, 0, 2) == '_:' ? 'bnode' : 'uri', 'value' => $resource2 ) ); } /** Delete a literal value from a property of a resource * * Example: * $graph->delete("http://www.example.com", 'dc:title', 'Title of Page'); * * @param mixed $resource The resource to add data to * @param mixed $property The property name * @param mixed $value The value of the property * @param string $lang The language of the literal * * @return integer */ public function deleteLiteral($resource, $property, $value, $lang = null) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); $this->checkValueParam($value); if ($lang) { $value['lang'] = $lang; } return $this->delete($resource, $property, $value); } /** This function is for internal use only. * * Deletes an inverse property from a resource. * * @ignore */ protected function deleteInverse($resource, $property, $value) { if (isset($this->revIndex[$resource])) { foreach ($this->revIndex[$resource][$property] as $k => $v) { if ($v['value'] === $value) { unset($this->revIndex[$resource][$property][$k]); } } if (count($this->revIndex[$resource][$property]) == 0) { unset($this->revIndex[$resource][$property]); } if (count($this->revIndex[$resource]) == 0) { unset($this->revIndex[$resource]); } } } /** Check if the graph contains any statements * * @return boolean True if the graph contains no statements */ public function isEmpty() { return count($this->index) == 0; } /** Get a list of all the shortened property names (qnames) for a resource. * * This method will return an empty array if the resource has no properties. * * @param string $resource * * @return array Array of shortened URIs */ public function properties($resource) { $this->checkResourceParam($resource); $properties = array(); if (isset($this->index[$resource])) { foreach ($this->index[$resource] as $property => $value) { $short = RdfNamespace::shorten($property); if ($short) { $properties[] = $short; } } } return $properties; } /** Get a list of the full URIs for the properties of a resource. * * This method will return an empty array if the resource has no properties. * * @param string $resource * * @return array Array of full URIs */ public function propertyUris($resource) { $this->checkResourceParam($resource); if (isset($this->index[$resource])) { return array_keys($this->index[$resource]); } else { return array(); } } /** Get a list of the full URIs for the properties that point to a resource. * * @param string $resource * * @return array Array of full property URIs */ public function reversePropertyUris($resource) { $this->checkResourceParam($resource); if (isset($this->revIndex[$resource])) { return array_keys($this->revIndex[$resource]); } else { return array(); } } /** Check to see if a property exists for a resource. * * This method will return true if the property exists. * If the value parameter is given, then it will only return true * if the value also exists for that property. * * By providing a value parameter you can use this function to check * to see if a triple exists in the graph. * * @param mixed $resource The resource to check * @param string $property The name of the property (e.g. foaf:name) * @param mixed $value An optional value of the property * * @return boolean True if value the property exists. */ public function hasProperty($resource, $property, $value = null) { $this->checkResourceParam($resource); $this->checkSinglePropertyParam($property, $inverse); $this->checkValueParam($value); // Use the reverse index if it is an inverse property if ($inverse) { $index = &$this->revIndex; } else { $index = &$this->index; } if (isset($index[$resource][$property])) { if (is_null($value)) { return true; } else { foreach ($index[$resource][$property] as $v) { if ($v == $value) { return true; } } } } return false; } /** Serialise the graph into RDF * * The $format parameter can be an EasyRdf\Format object, a * format name, a mime type or a file extension. * * Example: * $turtle = $graph->serialise('turtle'); * * @param mixed $format The format to serialise to * @param array $options Serialiser-specific options, for fine-tuning the output * * @return mixed The serialised graph */ public function serialise($format, array $options = array()) { if (!$format instanceof Format) { $format = Format::getFormat($format); } $serialiser = $format->newSerialiser(); return $serialiser->serialise($this, $format->getName(), $options); } /** Return a human readable view of all the resources in the graph * * This method is intended to be a debugging aid and will * return a pretty-print view of all the resources and their * properties. * * @param string $format Either 'html' or 'text' * * @return string */ public function dump($format = 'html') { $result = ''; if ($format == 'html') { $result .= "
". "Graph: ". $this->uri . "
\n"; } else { $result .= "Graph: ". $this->uri . "\n"; } foreach ($this->index as $resource => $properties) { $result .= $this->dumpResource($resource, $format); } return $result; } /** Return a human readable view of a resource and its properties * * This method is intended to be a debugging aid and will * print a resource and its properties. * * @param mixed $resource The resource to dump * @param string $format Either 'html' or 'text' * * @return string */ public function dumpResource($resource, $format = 'html') { $this->checkResourceParam($resource, true); if (isset($this->index[$resource])) { $properties = $this->index[$resource]; } else { return ''; } $plist = array(); foreach ($properties as $property => $values) { $olist = array(); foreach ($values as $value) { if ($value['type'] == 'literal') { $olist []= Utils::dumpLiteralValue($value, $format, 'black'); } else { $olist []= Utils::dumpResourceValue($value['value'], $format, 'blue'); } } $pstr = RdfNamespace::shorten($property); if ($pstr == null) { $pstr = $property; } if ($format == 'html') { $plist []= " ". "". htmlentities($pstr) . " ". " ". join(", ", $olist); } else { $plist []= " -> $pstr -> " . join(", ", $olist); } } if ($format == 'html') { return "
\n". "
".Utils::dumpResourceValue($resource, $format, 'blue')." ". "(". $this->classForResource($resource).")
\n". "
\n". "
".join("
\n
", $plist)."
". "
\n"; } else { return $resource." (".$this->classForResource($resource).")\n" . join("\n", $plist) . "\n\n"; } } /** Get the resource type of the graph * * The type will be a shortened URI as a string. * If the graph has multiple types then the type returned * may be arbitrary. * This method will return null if the resource has no type. * * @param string|null $resource * * @return string A type assocated with the resource (e.g. foaf:Document) */ public function type($resource = null) { $type = $this->typeAsResource($resource); if ($type) { return RdfNamespace::shorten($type); } return null; } /** Get the resource type of the graph as a EasyRdf\Resource * * If the graph has multiple types then the type returned * may be arbitrary. * This method will return null if the resource has no type. * * @param mixed $resource * * @return \EasyRdf\Resource A type associated with the resource */ public function typeAsResource($resource = null) { $this->checkResourceParam($resource, true); if ($resource) { return $this->get($resource, 'rdf:type', 'resource'); } return null; } /** Get a list of types for a resource * * The types will each be a shortened URI as a string. * This method will return an empty array if the resource has no types. * * If $resource is null, then it will get the types for the URI of the graph. * * @param string|null $resource * * @return array All types assocated with the resource (e.g. foaf:Person) */ public function types($resource = null) { $resources = $this->typesAsResources($resource); $types = array(); foreach ($resources as $type) { $types[] = RdfNamespace::shorten($type); } return $types; } /** * Get the resource types of the graph as a EasyRdf\Resource * * @param string|null $resource * * @return Resource[] */ public function typesAsResources($resource = null) { $this->checkResourceParam($resource, true); if ($resource) { return $this->all($resource, 'rdf:type', 'resource'); } return array(); } /** Check if a resource is of the specified type * * @param string $resource The resource to check the type of * @param string $type The type to check (e.g. foaf:Person) * * @return boolean True if resource is of specified type */ public function isA($resource, $type) { $this->checkResourceParam($resource, true); $this->checkResourceParam($type, true); foreach ($this->all($resource, 'rdf:type', 'resource') as $t) { if ($t->getUri() == $type) { return true; } } return false; } /** Add one or more rdf:type properties to a resource * * @param string $resource The resource to add the type to * @param string $types One or more types to add (e.g. foaf:Person) * * @return integer The number of types added */ public function addType($resource, $types) { $this->checkResourceParam($resource, true); if (!is_array($types)) { $types = array($types); } $count = 0; foreach ($types as $type) { $type = RdfNamespace::expand($type); $count += $this->add($resource, 'rdf:type', array('type' => 'uri', 'value' => $type)); } return $count; } /** Change the rdf:type property for a resource * * Note that if the resource object has already previously * been created, then the PHP class of the resource will not change. * * @param string $resource The resource to change the type of * @param string $type The new type (e.g. foaf:Person) * * @return integer The number of types added */ public function setType($resource, $type) { $this->checkResourceParam($resource, true); $this->delete($resource, 'rdf:type'); return $this->addType($resource, $type); } /** Get a human readable label for a resource * * This method will check a number of properties for a resource * (in the order: skos:prefLabel, rdfs:label, foaf:name, dc:title) * and return an approriate first that is available. If no label * is available then it will return null. * * @param string|null $resource * @param string|null $lang * * @return string A label for the resource. */ public function label($resource = null, $lang = null) { $this->checkResourceParam($resource, true); if ($resource) { return $this->get( $resource, 'skos:prefLabel|rdfs:label|foaf:name|rss:title|dc:title|dc11:title', 'literal', $lang ); } else { return null; } } /** Get the primary topic of the graph * * @param mixed $resource * * @return \EasyRdf\Resource The primary topic of the document. */ public function primaryTopic($resource = null) { $this->checkResourceParam($resource, true); if ($resource) { return $this->get( $resource, 'foaf:primaryTopic|^foaf:isPrimaryTopicOf', 'resource' ); } else { return null; } } /** Returns the graph as a RDF/PHP associative array * * @return array The contents of the graph as an array. */ public function toRdfPhp() { return $this->index; } /** Calculates the number of triples in the graph * * @return integer The number of triples in the graph. */ public function countTriples() { $count = 0; foreach ($this->index as $resource) { foreach ($resource as $values) { $count += count($values); } } return $count; } /** Magic method to return URI of resource when casted to string * * @return string The URI of the resource */ public function __toString() { return $this->uri == null ? '' : $this->uri; } /** Magic method to get a property of the graph * * Note that only properties in the default namespace can be accessed in this way. * * Example: * $value = $graph->title; * * @see RdfNamespace::setDefault() * @param string $name The name of the property * * @return string A single value for the named property */ public function __get($name) { return $this->get($this->uri, $name); } /** Magic method to set the value for a property of the graph * * Note that only properties in the default namespace can be accessed in this way. * * Example: * $graph->title = 'Title'; * * @see RdfNamespace::setDefault() * @param string $name The name of the property * @param string $value The value for the property * * @return integer */ public function __set($name, $value) { return $this->set($this->uri, $name, $value); } /** Magic method to check if a property exists * * Note that only properties in the default namespace can be accessed in this way. * * Example: * if (isset($graph->title)) { blah(); } * * @see RdfNamespace::setDefault() * @param string $name The name of the property * * @return boolean */ public function __isset($name) { return $this->hasProperty($this->uri, $name); } /** Magic method to delete a property of the graph * * Note that only properties in the default namespace can be accessed in this way. * * Example: * unset($graph->title); * * @see RdfNamespace::setDefault() * @param string $name The name of the property * * @return integer */ public function __unset($name) { return $this->delete($this->uri, $name); } } easyrdf-1.0.0/lib/GraphStore.php000066400000000000000000000240641370344162000165100ustar00rootroot00000000000000uri = $uri; $this->parsedUri = new ParsedUri($uri); } /** Get the URI of the graph store * * @return string The URI of the graph store */ public function getUri() { return $this->uri; } /** Fetch a named graph from the graph store * * The URI can either be a full absolute URI or * a URI relative to the URI of the graph store. * * @param string $uriRef The URI of graph desired * * @return Graph The graph requested */ public function get($uriRef) { if ($uriRef === self::DEFAULT_GRAPH) { $dataUrl = $this->urlForGraph(self::DEFAULT_GRAPH); $graph = new Graph(); } else { $graphUri = $this->parsedUri->resolve($uriRef)->toString(); $dataUrl = $this->urlForGraph($graphUri); $graph = new Graph($graphUri); } $graph->load($dataUrl); return $graph; } /** * Fetch default graph from the graph store * @return Graph */ public function getDefault() { return $this->get(self::DEFAULT_GRAPH); } /** Send some graph data to the graph store * * This method is used by insert() and replace() * * @ignore */ protected function sendGraph($method, $graph, $uriRef, $format) { if (is_object($graph) and $graph instanceof Graph) { if ($uriRef === null) { $uriRef = $graph->getUri(); } $data = $graph->serialise($format); } else { $data = $graph; } if ($uriRef === null) { throw new \InvalidArgumentException('Graph IRI is not specified'); } $formatObj = Format::getFormat($format); $mimeType = $formatObj->getDefaultMimeType(); if ($uriRef === self::DEFAULT_GRAPH) { $dataUrl = $this->urlForGraph(self::DEFAULT_GRAPH); } else { $graphUri = $this->parsedUri->resolve($uriRef)->toString(); $dataUrl = $this->urlForGraph($graphUri); } $client = Http::getDefaultHttpClient(); $client->resetParameters(true); $client->setUri($dataUrl); $client->setMethod($method); $client->setRawData($data); $client->setHeaders('Content-Type', $mimeType); $response = $client->request(); if (!$response->isSuccessful()) { throw new Exception( "HTTP request for {$dataUrl} failed: ".$response->getMessage() ); } return $response; } /** Replace the contents of a graph in the graph store with new data * * The $graph parameter is the EasyRdf\Graph object to be sent to the * graph store. Alternatively it can be a string, already serialised. * * The URI can either be a full absolute URI or * a URI relative to the URI of the graph store. * * The $format parameter can be given to specify the serialisation * used to send the graph data to the graph store. * * @param Graph|string $graph Data * @param string $uriRef The URI of graph to be replaced * @param string $format The format of the data to send to the graph store * * @return Http\Response The response from the graph store */ public function replace($graph, $uriRef = null, $format = 'ntriples') { return $this->sendGraph('PUT', $graph, $uriRef, $format); } /** * Replace the contents of default graph in the graph store with new data * * The $graph parameter is the EasyRdf\Graph object to be sent to the * graph store. Alternatively it can be a string, already serialised. * * The $format parameter can be given to specify the serialisation * used to send the graph data to the graph store. * * @param Graph|string $graph Data * @param string $format The format of the data to send to the graph store * * @return Http\Response The response from the graph store */ public function replaceDefault($graph, $format = 'ntriples') { return self::replace($graph, self::DEFAULT_GRAPH, $format); } /** Add data to a graph in the graph store * * The $graph parameter is the EasyRdf\Graph object to be sent to the * graph store. Alternatively it can be a string, already serialised. * * The URI can either be a full absolute URI or * a URI relative to the URI of the graph store. * * The $format parameter can be given to specify the serialisation * used to send the graph data to the graph store. * * @param Graph|string $graph Data * @param string $uriRef The URI of graph to be added to * @param string $format The format of the data to send to the graph store * * @return Http\Response The response from the graph store */ public function insert($graph, $uriRef = null, $format = 'ntriples') { return $this->sendGraph('POST', $graph, $uriRef, $format); } /** * Add data to default graph of the graph store * * The $graph parameter is the EasyRdf\Graph object to be sent to the * graph store. Alternatively it can be a string, already serialised. * * The $format parameter can be given to specify the serialisation * used to send the graph data to the graph store. * * @param Graph|string $graph Data * @param string $format The format of the data to send to the graph store * * @return Http\Response The response from the graph store */ public function insertIntoDefault($graph, $format = 'ntriples') { return $this->insert($graph, self::DEFAULT_GRAPH, $format); } /** Delete named graph content from the graph store * * The URI can either be a full absolute URI or * a URI relative to the URI of the graph store. * * @param string $uriRef The URI of graph to be added to * * @throws Exception * @return Http\Response The response from the graph store */ public function delete($uriRef) { if ($uriRef === self::DEFAULT_GRAPH) { $dataUrl = $this->urlForGraph(self::DEFAULT_GRAPH); } else { $graphUri = $this->parsedUri->resolve($uriRef)->toString(); $dataUrl = $this->urlForGraph($graphUri); } $client = Http::getDefaultHttpClient(); $client->resetParameters(true); $client->setUri($dataUrl); $client->setMethod('DELETE'); $response = $client->request(); if (!$response->isSuccessful()) { throw new Exception( "HTTP request to delete {$dataUrl} failed: ".$response->getMessage() ); } return $response; } /** * Delete default graph content from the graph store * * @return Http\Response * @throws Exception */ public function deleteDefault() { return $this->delete(self::DEFAULT_GRAPH); } /** Work out the full URL for a graph store request. * by checking if if it is a direct or indirect request. * @ignore */ protected function urlForGraph($url) { if ($url === self::DEFAULT_GRAPH) { $url = $this->uri.'?default'; } elseif (strpos($url, $this->uri) === false) { $url = $this->uri."?graph=".urlencode($url); } return $url; } /** Magic method to return URI of the graph store when casted to string * * @return string The URI of the graph store */ public function __toString() { return empty($this->uri) ? '' : $this->uri; } } easyrdf-1.0.0/lib/Http.php000066400000000000000000000063401370344162000153460ustar00rootroot00000000000000 5, 'useragent' => 'EasyRdf HTTP Client', 'timeout' => 10 ); /** * Request URI * * @var string */ private $uri = null; /** * Associative array of request headers * * @var array */ private $headers = array(); /** * HTTP request method * * @var string */ private $method = 'GET'; /** * Associative array of GET parameters * * @var array */ private $paramsGet = array(); /** * The raw post data to send. Could be set by setRawData($data). * * @var string */ private $rawPostData = null; /** * Redirection counter * * @var int */ private $redirectCounter = 0; /** * Constructor method. Will create a new HTTP client. Accepts the target * URL and optionally configuration array. * * @param string $uri * @param array $config Configuration key-value pairs. */ public function __construct($uri = null, $config = null) { if ($uri !== null) { $this->setUri($uri); } if ($config !== null) { $this->setConfig($config); } } /** * Set the URI for the next request * * @param string $uri * * @throws \InvalidArgumentException * @return self */ public function setUri($uri) { if (!is_string($uri)) { $uri = strval($uri); } if (!preg_match('/^http(s?):/', $uri)) { throw new \InvalidArgumentException( "EasyRdf\\Http\\Client only supports the 'http' and 'https' schemes." ); } $this->uri = $uri; return $this; } /** * Get the URI for the next request * * @param bool $asString * * @return string */ public function getUri($asString = true) { return $this->uri; } /** * Set configuration parameters for this HTTP client * * @param array $config * * @return self * @throws \InvalidArgumentException */ public function setConfig($config = array()) { if ($config == null or !is_array($config)) { throw new \InvalidArgumentException( "\$config should be an array and cannot be null" ); } foreach ($config as $k => $v) { $this->config[strtolower($k)] = $v; } return $this; } /** * Set a request header * * @param string $name Header name (e.g. 'Accept') * @param string $value Header value or null * * @return self */ public function setHeaders($name, $value = null) { $normalizedName = strtolower($name); // If $value is null or false, unset the header if ($value === null || $value === false) { unset($this->headers[$normalizedName]); } else { // Else, set the header $this->headers[$normalizedName] = array($name, $value); } return $this; } /** * Set the next request's method * * Validated the passed method and sets it. * * @param string $method * * @return self * @throws \InvalidArgumentException */ public function setMethod($method) { if (!is_string($method) or !preg_match('/^[A-Z]+$/', $method)) { throw new \InvalidArgumentException("Invalid HTTP request method."); } $this->method = $method; return $this; } /** * Get the method for the next request * * @return string */ public function getMethod() { return $this->method; } /** * Get the value of a specific header * * Note that if the header has more than one value, an array * will be returned. * * @param string $key * * @return string|array|null The header value or null if it is not set */ public function getHeader($key) { $key = strtolower($key); if (isset($this->headers[$key])) { return $this->headers[$key][1]; } else { return null; } } /** * Set a GET parameter for the request. * * @param string $name * @param string $value * * @return self */ public function setParameterGet($name, $value = null) { if ($value === null) { if (isset($this->paramsGet[$name])) { unset($this->paramsGet[$name]); } } else { $this->paramsGet[$name] = $value; } return $this; } /** * Get a GET parameter for the request. * * @param string $name * * @return string value */ public function getParameterGet($name) { if (isset($this->paramsGet[$name])) { return $this->paramsGet[$name]; } else { return null; } } /** * Get all the GET parameters * * @return array */ public function getParametersGet() { return $this->paramsGet; } /** * Get the number of redirections done on the last request * * @return int */ public function getRedirectionsCount() { return $this->redirectCounter; } /** * Set the raw (already encoded) POST data. * * This function is here for two reasons: * 1. For advanced user who would like to set their own data, already encoded * 2. For backwards compatibilty: If someone uses the old post($data) method. * this method will be used to set the encoded data. * * $data can also be stream (such as file) from which the data will be read. * * @param string|resource $data * * @return self */ public function setRawData($data) { $this->rawPostData = $data; return $this; } /** * Get the raw (already encoded) POST data. * * @return string */ public function getRawData() { return $this->rawPostData; } /** * Clear all GET and POST parameters * * Should be used to reset the request parameters if the client is * used for several concurrent requests. * * clearAll parameter controls if we clean just parameters or also * headers * * @param bool $clearAll Should all data be cleared? * * @return self */ public function resetParameters($clearAll = false) { // Reset parameter data $this->paramsGet = array(); $this->rawPostData = null; $this->method = 'GET'; if ($clearAll) { $this->headers = array(); } else { // Clear outdated headers if (isset($this->headers['content-type'])) { unset($this->headers['content-type']); } if (isset($this->headers['content-length'])) { unset($this->headers['content-length']); } } return $this; } /** * Send the HTTP request and return an HTTP response object * * @param null|string $method * * @throws \EasyRdf\Exception * @return Response */ public function request($method = null) { if (!$this->uri) { throw new Exception( "Set URI before calling Client->request()" ); } if ($method) { $this->setMethod($method); } $this->redirectCounter = 0; $response = null; // Send the first request. If redirected, continue. do { // Clone the URI and add the additional GET parameters to it $uri = parse_url($this->uri); if ($uri['scheme'] === 'http') { $host = $uri['host']; } elseif ($uri['scheme'] === 'https') { $host = 'ssl://'.$uri['host']; } else { throw new Exception( "Unsupported URI scheme: ".$uri['scheme'] ); } if (isset($uri['port'])) { $port = $uri['port']; } else { if ($uri['scheme'] === 'https') { $port = 443; } else { $port = 80; } } if (!empty($this->paramsGet)) { if (!empty($uri['query'])) { $uri['query'] .= '&'; } else { $uri['query'] = ''; } $uri['query'] .= http_build_query($this->paramsGet, null, '&'); } $headers = $this->prepareHeaders($uri['host'], $port); // Open socket to remote server $socket = @fsockopen($host, $port, $errno, $errstr, $this->config['timeout']); if (!$socket) { throw new Exception("Unable to connect to $host:$port ($errstr)"); } stream_set_timeout($socket, $this->config['timeout']); $info = stream_get_meta_data($socket); // Write the request $path = $uri['path']; if (empty($path)) { $path = '/'; } if (isset($uri['query'])) { $path .= '?' . $uri['query']; } fwrite($socket, "{$this->method} {$path} HTTP/1.1\r\n"); foreach ($headers as $k => $v) { if (is_string($k)) { $v = ucfirst($k) . ": $v"; } fwrite($socket, "$v\r\n"); } fwrite($socket, "\r\n"); // Send the request body, if there is one set if (isset($this->rawPostData)) { fwrite($socket, $this->rawPostData); } // Read in the response $content = ''; while (!feof($socket) && !$info['timed_out']) { $content .= fgets($socket); $info = stream_get_meta_data($socket); } if ($info['timed_out']) { throw new Exception("Request to $host:$port timed out"); } // FIXME: support HTTP/1.1 100 Continue // Close the socket @fclose($socket); // Parse the response string $response = Response::fromString($content); // If we got redirected, look for the Location header if ($response->isRedirect() && ($location = $response->getHeader('location')) ) { // Avoid problems with buggy servers that add whitespace at the // end of some headers (See ZF-11283) $location = trim($location); // Some servers return relative URLs in the location header // resolve it in relation to previous request $baseUri = new ParsedUri($this->uri); $location = $baseUri->resolve($location)->toString(); // If it is a 303 then drop the parameters and send a GET request if ($response->getStatus() == 303) { $this->resetParameters(); $this->setMethod('GET'); } // If we got a well formed absolute URI if (parse_url($location)) { $this->setHeaders('host', null); $this->setUri($location); } else { throw new Exception( "Failed to parse Location header returned by ". $this->uri ); } ++$this->redirectCounter; } else { // If we didn't get any location, stop redirecting break; } } while ($this->redirectCounter < $this->config['maxredirects']); return $response; } /** * Prepare the request headers * * @ignore * * @param $host * @param $port * * @return array */ protected function prepareHeaders($host, $port) { $headers = array(); // Set the host header if (! isset($this->headers['host'])) { // If the port is not default, add it if ($port !== 80 and $port !== 443) { $host .= ':' . $port; } $headers[] = "Host: {$host}"; } // Set the connection header if (! isset($this->headers['connection'])) { $headers[] = "Connection: close"; } // Set the user agent header if (! isset($this->headers['user-agent'])) { $headers[] = "User-Agent: {$this->config['useragent']}"; } // If we have rawPostData set, set the content-length header if (isset($this->rawPostData)) { $headers[] = "Content-Length: ".strlen($this->rawPostData); } // Add all other user defined headers foreach ($this->headers as $header) { list($name, $value) = $header; if (is_array($value)) { $value = implode(', ', $value); } $headers[] = "$name: $value"; } return $headers; } } easyrdf-1.0.0/lib/Http/Exception.php000066400000000000000000000005561370344162000173070ustar00rootroot00000000000000body = $body; } public function getBody() { return $this->body; } } easyrdf-1.0.0/lib/Http/Response.php000066400000000000000000000265101370344162000171450ustar00rootroot00000000000000status = (int) $status; $this->body = $body; $this->version = $version; $this->message = $message; foreach ($headers as $k => $v) { $k = ucwords(strtolower($k)); $this->headers[$k] = $v; } } /** * Check whether the response in successful * * @return boolean */ public function isSuccessful() { return ($this->status >= 200 && $this->status < 300); } /** * Check whether the response is an error * * @return boolean */ public function isError() { return ($this->status >= 400 && $this->status < 600); } /** * Check whether the response is a redirection * * @return boolean */ public function isRedirect() { return ($this->status >= 300 && $this->status < 400); } /** * Get the HTTP response status code * * @return int */ public function getStatus() { return $this->status; } /** * Return a message describing the HTTP response code * (Eg. "OK", "Not Found", "Moved Permanently") * * @return string */ public function getMessage() { return $this->message; } /** * Get the response body as string * * @return string */ public function getBody() { $body = $this->body; if ('chunked' === strtolower($this->getHeader('transfer-encoding'))) { $body = self::decodeChunkedBody($body); } $contentEncoding = strtolower($this->getHeader('content-encoding')); if ('gzip' === $contentEncoding) { $body = self::decodeGzip($body); } elseif ('deflate' === $contentEncoding) { $body = self::decodeDeflate($body); } return $body; } /** * Get the raw response body (as transfered "on wire") as string * * If the body is encoded (with Transfer-Encoding, not content-encoding - * IE "chunked" body), gzip compressed, etc. it will not be decoded. * * @return string */ public function getRawBody() { return $this->body; } /** * Get the HTTP version of the response * * @return string */ public function getVersion() { return $this->version; } /** * Get the response headers * * @return array */ public function getHeaders() { return $this->headers; } /** * Get a specific header as string, or null if it is not set * * @param string $header * * @return string|array|null */ public function getHeader($header) { $header = ucwords(strtolower($header)); if (array_key_exists($header, $this->headers)) { return $this->headers[$header]; } return null; } /** * Get all headers as string * * @param boolean $statusLine Whether to return the first status line (ie "HTTP 200 OK") * @param string $br Line breaks (eg. "\n", "\r\n", "
") * * @return string */ public function getHeadersAsString($statusLine = true, $br = "\n") { $str = ''; if ($statusLine) { $str = "HTTP/{$this->version} {$this->status} {$this->message}{$br}"; } // Iterate over the headers and stringify them foreach ($this->headers as $name => $value) { if (is_string($value)) { $str .= "{$name}: {$value}{$br}"; } elseif (is_array($value)) { foreach ($value as $subval) { $str .= "{$name}: {$subval}{$br}"; } } } return $str; } /** * Create an EasyRdf\Http\Response object from a HTTP response string * * @param string $responseStr * * @throws \EasyRdf\Exception * @return self */ public static function fromString($responseStr) { // First, split body and headers $matches = preg_split('|(?:\r?\n){2}|m', $responseStr, 2); if ($matches and 2 === count($matches)) { list ($headerLines, $body) = $matches; } else { throw new Exception( "Failed to parse HTTP response." ); } // Split headers part to lines $headerLines = preg_split('|[\r\n]+|m', $headerLines); $status = array_shift($headerLines); if (preg_match("|^HTTP\/([\d\.x]+) (\d+) ?([^\r\n]*)|", $status, $m)) { $version = $m[1]; $status = $m[2]; $message = $m[3] ? $m[3] : null; } else { throw new Exception( "Failed to parse HTTP response status line." ); } // Process the rest of the header lines $headers = array(); foreach ($headerLines as $line) { if (preg_match("|^([\w-]+):\s+(.+)$|", $line, $m)) { $hName = ucwords(strtolower($m[1])); $hValue = $m[2]; if (isset($headers[$hName])) { if (! is_array($headers[$hName])) { $headers[$hName] = array($headers[$hName]); } $headers[$hName][] = $hValue; } else { $headers[$hName] = $hValue; } } } return new self($status, $headers, $body, $version, $message); } /** * Decode a "chunked" transfer-encoded body and return the decoded text * * @param string $body * * @throws \EasyRdf\Exception * * @return string */ public static function decodeChunkedBody($body) { $decBody = ''; while (trim($body)) { if (!preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm", $body, $m)) { throw new Exception( "Error parsing body - doesn't seem to be a chunked message" ); } $length = hexdec(trim($m[1])); $cut = strlen($m[0]); $decBody .= substr($body, $cut, $length); $body = substr($body, $cut + $length + 2); } return $decBody; } /** * Decode a gzip encoded message (when Content-encoding = gzip) * * Currently requires PHP with zlib support * * @param string $body * * @throws Exception * * @return string */ public static function decodeGzip($body) { if (!function_exists('gzinflate')) { throw new Exception( 'zlib extension is required in order to decode "gzip" encoding' ); } return gzinflate(substr($body, 10)); } /** * Decode a zlib deflated message (when Content-encoding = deflate) * * Currently requires PHP with zlib support * * @param string $body * * @throws Exception * * @return string */ public static function decodeDeflate($body) { if (!function_exists('gzuncompress')) { throw new Exception( 'zlib extension is required in order to decode "deflate" encoding' ); } /** * Some servers (IIS ?) send a broken deflate response, without the * RFC-required zlib header. * * We try to detect the zlib header, and if it does not exist we * teat the body is plain DEFLATE content. * * This method was adapted from PEAR HTTP_Request2 by (c) Alexey Borzov * * @link http://framework.zend.com/issues/browse/ZF-6040 */ $zlibHeader = unpack('n', substr($body, 0, 2)); if ($zlibHeader[1] % 31 === 0) { return gzuncompress($body); } return gzinflate($body); } /** * Get the entire response as string * * @param string $br Line breaks (eg. "\n", "\r\n", "
") * * @return string */ public function asString($br = "\n") { return $this->getHeadersAsString(true, $br) . $br . $this->getRawBody(); } /** * Implements magic __toString() * * @return string */ public function __toString() { return $this->asString(); } } easyrdf-1.0.0/lib/Isomorphic.php000066400000000000000000000375771370344162000165630ustar00rootroot00000000000000 0 or count($bnodesB) > 0) { // There are blank nodes - build a bi-jection return self::buildBijectionTo($statementsA, $bnodesA, $statementsB, $bnodesB); } else { // No bnodes and the grounded statements match return array(); } } /** * Count the number of subjects in a graph * @ignore */ private static function countSubjects($graph) { return count($graph->toRdfPhp()); } /** * Check if all the statements in $graphA also appear in $graphB * @ignore */ private static function groundedStatementsMatch($graphA, $graphB, &$bnodes, &$anonStatements) { $groundedStatementsMatch = true; foreach ($graphA->toRdfPhp() as $subject => $properties) { if (substr($subject, 0, 2) == '_:') { array_push($bnodes, $subject); $subjectIsBnode = true; } else { $subjectIsBnode = false; } foreach ($properties as $property => $values) { foreach ($values as $value) { if ($value['type'] == 'uri' and substr($value['value'], 0, 2) == '_:') { array_push($bnodes, $value['value']); $objectIsBnode = true; } else { $objectIsBnode = false; } if ($groundedStatementsMatch and $subjectIsBnode === false and $objectIsBnode === false and $graphB->hasProperty($subject, $property, $value) === false ) { $groundedStatementsMatch = false; } if ($subjectIsBnode or $objectIsBnode) { array_push( $anonStatements, array( array('type' => $subjectIsBnode ? 'bnode' : 'uri', 'value' => $subject), array('type' => 'uri', 'value' => $property), $value ) ); } } } } return $groundedStatementsMatch; } /** * The main recursive bijection algorithm. * * This algorithm is very similar to the one explained by Jeremy Carroll in * http://www.hpl.hp.com/techreports/2001/HPL-2001-293.pdf. Page 12 has the * relevant pseudocode. * * @ignore */ private static function buildBijectionTo( $statementsA, $nodesA, $statementsB, $nodesB, $groundedHashesA = array(), $groundedHashesB = array() ) { // Create a hash signature of every node, based on the signature of // statements it exists in. // We also save hashes of nodes that cannot be reliably known; we will use // that information to eliminate possible recursion combinations. // // Any mappings given in the method parameters are considered grounded. list($hashesA, $ungroundedHashesA) = self::hashNodes($statementsA, $nodesA, $groundedHashesA); list($hashesB, $ungroundedHashesB) = self::hashNodes($statementsB, $nodesB, $groundedHashesB); // Grounded hashes are built at the same rate between the two graphs (if // they are isomorphic). If there exists a grounded node in one that is // not in the other, we can just return. Ungrounded nodes might still // conflict, so we don't check them. This is a little bit messy in the // middle of the method, and probably slows down isomorphic checks, but // prevents almost-isomorphic cases from getting nutty. foreach ($hashesA as $hashA) { if (!in_array($hashA, $hashesB)) { return null; } } foreach ($hashesB as $hashB) { if (!in_array($hashB, $hashesA)) { return null; } } // Using the created hashes, map nodes to other_nodes // Ungrounded hashes will also be equal, but we keep the distinction // around for when we recurse later (we only recurse on ungrounded nodes) $bijection = array(); foreach ($nodesA as $nodeA) { $foundNode = null; foreach ($ungroundedHashesB as $nodeB => $hashB) { if ($ungroundedHashesA[$nodeA] == $hashB) { $foundNode = $nodeB; } } if ($foundNode) { $bijection[$nodeA] = $foundNode; // Deletion is required to keep counts even; two nodes with identical // signatures can biject to each other at random. unset($ungroundedHashesB[$foundNode]); } } // bijection is now a mapping of nodes to other_nodes. If all are // accounted for on both sides, we have a bijection. // // If not, we will speculatively mark pairs with matching ungrounded // hashes as bijected and recurse. $bijectionA = array_keys($bijection); $bijectionB = array_values($bijection); sort($bijectionA); sort($nodesA); sort($bijectionB); sort($nodesB); if ($bijectionA != $nodesA or $bijectionB != $nodesB) { $bijection = null; foreach ($nodesA as $nodeA) { // We don't replace grounded nodes' hashes if (isset($hashesA[$nodeA])) { continue; } foreach ($nodesB as $nodeB) { // We don't replace grounded nodesB's hashes if (isset($hashesB[$nodeB])) { continue; } // The ungrounded signature must match for this to potentially work if ($ungroundedHashesA[$nodeA] != $ungroundedHashesB[$nodeB]) { continue; } $hash = sha1($nodeA); $hashesA[$nodeA] = $hash; $hashesB[$nodeB] = $hash; $bijection = self::buildBijectionTo( $statementsA, $nodesA, $statementsB, $nodesA, $hashesA, $hashesB ); } } } return $bijection; } /** * Given a set of statements, create a mapping of node => SHA1 for a given * set of blank nodes. grounded_hashes is a mapping of node => SHA1 pairs * that we will take as a given, and use those to make more specific * signatures of other nodes. * * Returns a tuple of associative arrats: one of grounded hashes, and one of all * hashes. grounded hashes are based on non-blank nodes and grounded blank * nodes, and can be used to determine if a node's signature matches * another. * * @ignore */ private static function hashNodes($statements, $nodes, $groundedHahes) { $hashes = $groundedHahes; $ungroundedHashes = array(); $hashNeeded = true; // We may have to go over the list multiple times. If a node is marked as // grounded, other nodes can then use it to decide their own state of // grounded. while ($hashNeeded) { $startingGroundedNodes = count($hashes); foreach ($nodes as $node) { if (!isset($hashes[$node])) { $hash = self::nodeHashFor($node, $statements, $hashes); if (self::nodeIsGrounded($node, $statements, $hashes)) { $hashes[$node] = $hash; } } $ungroundedHashes[$node] = $hash; } // after going over the list, any nodes with a unique hash can be marked // as grounded, even if we have not tied them back to a root yet. $uniques = array(); foreach ($ungroundedHashes as $node => $hash) { $uniques[$hash] = isset($uniques[$hash]) ? false : $node; } foreach ($uniques as $hash => $node) { if ($node) { $hashes[$node] = $hash; } } $hashNeeded = ($startingGroundedNodes != count($hashes)); } return array($hashes, $ungroundedHashes); } /** * Generate a hash for a node based on the signature of the statements it * appears in. Signatures consist of grounded elements in statements * associated with a node, that is, anything but an ungrounded anonymous * node. Creating the hash is simply hashing a sorted list of each * statement's signature, which is itself a concatenation of the string form * of all grounded elements. * * Nodes other than the given node are considered grounded if they are a * member in the given hash. * * Returns a tuple consisting of grounded being true or false and the string * for the hash * * @ignore */ private static function nodeHashFor($node, $statements, $hashes) { $statement_signatures = array(); foreach ($statements as $statement) { foreach ($statement as $n) { if ($n['type'] != 'literal' and $n['value'] == $node) { array_push( $statement_signatures, self::hashStringFor($statement, $hashes, $node) ); } } } // Note that we sort the signatures--without a canonical ordering, // we might get different hashes for equivalent nodes sort($statement_signatures); // Convert statements into one long string and hash it return sha1(implode('', $statement_signatures)); } /** * Returns true if a given node is grounded * A node is groundd if it is not a blank node or it is included * in the given mapping of grounded nodes. * * @ignore */ private static function nodeIsGrounded($node, $statements, $hashes) { $grounded = true; foreach ($statements as $statement) { if (in_array($node, $statement)) { foreach ($statement as $resource) { if ($node['type'] != 'bnode' or isset($hashes[$node['value']]) or $resource == $node ) { $grounded = false; } } } } return $grounded; } /** * Provide a string signature for the given statement, collecting * string signatures for grounded node elements. * * @ignore */ private static function hashStringFor($statement, $hashes, $node) { $str = ""; foreach ($statement as $r) { $str .= self::stringForNode($r, $hashes, $node); } return $str; } /** * Provides a string for the given node for use in a string signature * Non-anonymous nodes will return their string form. Grounded anonymous * nodes will return their hashed form. * * @ignore */ private static function stringForNode($node, $hashes, $target) { if (is_null($node)) { return ""; } elseif ($node['type'] == 'bnode') { if ($node['value'] == $target) { return "itself"; } elseif (isset($hashes[$node['value']])) { return $hashes[$node['value']]; } else { return "a blank node"; } } else { $s = new Serialiser\Ntriples(); return $s->serialiseValue($node); } } } easyrdf-1.0.0/lib/Literal.php000066400000000000000000000265141370344162000160300ustar00rootroot00000000000000value = $value; $this->lang = $lang ? $lang : null; $this->datatype = $datatype ? $datatype : null; if ($this->datatype) { if (is_object($this->datatype)) { // Convert objects to strings $this->datatype = strval($this->datatype); } else { // Expand shortened URIs (CURIEs) $this->datatype = RdfNamespace::expand($this->datatype); } // Literals can not have both a language and a datatype $this->lang = null; } else { // Set the datatype based on the subclass $class = get_class($this); if (isset(self::$classMap[$class])) { $this->datatype = self::$classMap[$class]; $this->lang = null; } } if (is_float($this->value)) { // special handling of floats, as they suffer from locale [mis]configuration $this->value = rtrim(sprintf('%F', $this->value), '0'); } else { // Cast value to string settype($this->value, 'string'); } } /** Returns the value of the literal. * * @return string Value of this literal. */ public function getValue() { return $this->value; } /** Returns the full datatype URI of the literal. * * @return string Datatype URI of this literal. */ public function getDatatypeUri() { return $this->datatype; } /** Returns the shortened datatype URI of the literal. * * @return string Datatype of this literal (e.g. xsd:integer). */ public function getDatatype() { if ($this->datatype) { return RdfNamespace::shorten($this->datatype); } else { return null; } } /** Returns the language of the literal. * * @return string Language of this literal. */ public function getLang() { return $this->lang; } /** Returns the properties of the literal as an associative array * * For example: * array('type' => 'literal', 'value' => 'string value') * * @return array The properties of the literal */ public function toRdfPhp() { $array = array( 'type' => 'literal', 'value' => $this->value ); if ($this->datatype) { $array['datatype'] = $this->datatype; } if ($this->lang) { $array['lang'] = $this->lang; } return $array; } /** Magic method to return the value of a literal as a string * * @return string The value of the literal */ public function __toString() { return isset($this->value) ? $this->value : ''; } /** Return pretty-print view of the literal * * @param string $format Either 'html' or 'text' * @param string $color The colour of the text * * @return string */ public function dumpValue($format = 'html', $color = 'black') { return Utils::dumpLiteralValue($this, $format, $color); } } /* Register default set of datatype classes */ Literal::setDatatypeMapping('xsd:boolean', 'EasyRdf\Literal\Boolean'); Literal::setDatatypeMapping('xsd:date', 'EasyRdf\Literal\Date'); Literal::setDatatypeMapping('xsd:dateTime', 'EasyRdf\Literal\DateTime'); Literal::setDatatypeMapping('xsd:decimal', 'EasyRdf\Literal\Decimal'); Literal::setDatatypeMapping('xsd:hexBinary', 'EasyRdf\Literal\HexBinary'); Literal::setDatatypeMapping('rdf:HTML', 'EasyRdf\Literal\HTML'); Literal::setDatatypeMapping('xsd:integer', 'EasyRdf\Literal\Integer'); Literal::setDatatypeMapping('rdf:XMLLiteral', 'EasyRdf\Literal\XML'); easyrdf-1.0.0/lib/Literal/000077500000000000000000000000001370344162000153075ustar00rootroot00000000000000easyrdf-1.0.0/lib/Literal/Boolean.php000066400000000000000000000066001370344162000174010ustar00rootroot00000000000000value) === 'true' or $this->value === '1'; } /** Return true if the value of the literal is 'true' or '1' * * @return bool */ public function isTrue() { return strtolower($this->value) === 'true' or $this->value === '1'; } /** Return true if the value of the literal is 'false' or '0' * * @return bool */ public function isFalse() { return strtolower($this->value) === 'false' or $this->value === '0'; } } easyrdf-1.0.0/lib/Literal/Date.php000066400000000000000000000104331370344162000166760ustar00rootroot00000000000000format('Y-m-d'); } parent::__construct($value, null, $datatype); } /** Parses a string using DateTime and creates a new literal * * Example: * $date = EasyRdf\Literal\Date::parse('1 January 2011'); * * @see DateTime * @param string $value The date to parse * * @return self */ public static function parse($value) { $value = new \DateTime($value); return new self($value); } /** Returns the date as a PHP DateTime object * * @see DateTime::format * @return string */ public function getValue() { return new \DateTime($this->value); } /** Returns date formatted according to given format * * @see DateTime::format * @param string $format * * @return string */ public function format($format) { return $this->getValue()->format($format); } /** A full integer representation of the year, 4 digits * * @return integer */ public function year() { return (int)$this->format('Y'); } /** Integer representation of the month * * @return integer */ public function month() { return (int)$this->format('m'); } /** Integer representation of the day of the month * * @return integer */ public function day() { return (int)$this->format('d'); } } easyrdf-1.0.0/lib/Literal/DateTime.php000066400000000000000000000076641370344162000175310ustar00rootroot00000000000000format(\DateTime::ATOM); $value = preg_replace('/[\+\-]00(\:?)00$/', 'Z', $atom); } Literal::__construct($value, null, $datatype); } /** Parses a string using DateTime and creates a new literal * * Example: * $dt = EasyRdf\Literal\DateTime::parse('Mon 18 Jul 2011 18:45:43 BST'); * * @see DateTime * @param string $value The date and time to parse * * @return self */ public static function parse($value) { $value = new \DateTime($value); return new self($value); } /** 24-hour format of the hour as an integer * * @return integer */ public function hour() { return (int)$this->format('H'); } /** The minutes pasts the hour as an integer * * @return integer */ public function min() { return (int)$this->format('i'); } /** The seconds pasts the minute as an integer * * @return integer */ public function sec() { return (int)$this->format('s'); } } easyrdf-1.0.0/lib/Literal/Decimal.php000066400000000000000000000106001370344162000173530ustar00rootroot00000000000000value); } /** * @param string $value * * @throws \UnexpectedValueException */ public static function validate($value) { if (!mb_ereg_match(self::DECIMAL_REGEX, $value)) { throw new \UnexpectedValueException("'{$value}' doesn't look like a valid decimal"); } } /** * Converts valid xsd:decimal literal to Canonical representation * see http://www.w3.org/TR/xmlschema-2/#decimal * * @param string $value Valid xsd:decimal literal * * @return string */ public static function canonicalise($value) { $pieces = array(); mb_ereg(self::DECIMAL_REGEX, $value, $pieces); $sign = $pieces[1] === '-' ? '-' : ''; // '+' is not allowed $integer = ltrim(($pieces[4] !== false) ? $pieces[4] : $pieces[7], '0'); $fractional = rtrim($pieces[5], '0'); if (empty($integer)) { $integer = '0'; } if (empty($fractional)) { $fractional = '0'; } return "{$sign}{$integer}.{$fractional}"; } } easyrdf-1.0.0/lib/Literal/HTML.php000066400000000000000000000055411370344162000165710ustar00rootroot00000000000000value, $allowableTags); } } easyrdf-1.0.0/lib/Literal/HexBinary.php000066400000000000000000000067101370344162000177150ustar00rootroot00000000000000value); } } easyrdf-1.0.0/lib/Literal/Integer.php000066400000000000000000000052261370344162000174220ustar00rootroot00000000000000value; } } easyrdf-1.0.0/lib/Literal/XML.php000066400000000000000000000054511370344162000164650ustar00rootroot00000000000000loadXML($this->value); return $dom; } } easyrdf-1.0.0/lib/ParsedUri.php000066400000000000000000000246331370344162000163320ustar00rootroot00000000000000scheme = isset($matches[2]) ? $matches[2] : ''; } if (!empty($matches[3])) { $this->authority = isset($matches[4]) ? $matches[4] : ''; } $this->path = isset($matches[5]) ? $matches[5] : ''; if (!empty($matches[6])) { $this->query = isset($matches[7]) ? $matches[7] : ''; } if (!empty($matches[8])) { $this->fragment = isset($matches[9]) ? $matches[9] : ''; } } } elseif (is_array($uri)) { $this->scheme = isset($uri['scheme']) ? $uri['scheme'] : null; $this->authority = isset($uri['authority']) ? $uri['authority'] : null; $this->path = isset($uri['path']) ? $uri['path'] : null; $this->query = isset($uri['query']) ? $uri['query'] : null; $this->fragment = isset($uri['fragment']) ? $uri['fragment'] : null; } } /** Returns true if this is an absolute (complete) URI * @return boolean */ public function isAbsolute() { return $this->scheme !== null; } /** Returns true if this is an relative (partial) URI * @return boolean */ public function isRelative() { return $this->scheme === null; } /** Returns the scheme of the URI (e.g. http) * @return string */ public function getScheme() { return $this->scheme; } /** Sets the scheme of the URI (e.g. http) * @param string $scheme The new value for the scheme of the URI */ public function setScheme($scheme) { $this->scheme = $scheme; } /** Returns the authority of the URI (e.g. www.example.com:8080) * @return string */ public function getAuthority() { return $this->authority; } /** Sets the authority of the URI (e.g. www.example.com:8080) * @param string $authority The new value for the authority component of the URI */ public function setAuthority($authority) { $this->authority = $authority; } /** Returns the path of the URI (e.g. /foo/bar) * @return string */ public function getPath() { return $this->path; } /** Set the path of the URI (e.g. /foo/bar) * @param string $path The new value for the path component of the URI */ public function setPath($path) { $this->path = $path; } /** Returns the query string part of the URI (e.g. foo=bar) * @return string */ public function getQuery() { return $this->query; } /** Set the query string of the URI (e.g. foo=bar) * @param string $query The new value for the query string component of the URI */ public function setQuery($query) { $this->query = $query; } /** Returns the fragment part of the URI (i.e. after the #) * @return string */ public function getFragment() { return $this->fragment; } /** Set the fragment of the URI (i.e. after the #) * @param string $fragment The new value for the fragment component of the URI */ public function setFragment($fragment) { $this->fragment = $fragment; } /** * Normalises the path of this URI if it has one. * * Normalising a path means that any unnecessary '.' and '..' segments are removed. For example, the * URI http://example.com/a/b/../c/./d would be normalised to http://example.com/a/c/d * * @return self */ public function normalise() { if (empty($this->path)) { return $this; } // Remove ./ from the start if (substr($this->path, 0, 2) == './') { // Remove both characters $this->path = substr($this->path, 2); } // Remove /. from the end if (substr($this->path, -2) == '/.') { // Remove only the last dot, not the slash! $this->path = substr($this->path, 0, -1); } if (substr($this->path, -3) == '/..') { $this->path .= '/'; } // Split the path into its segments $segments = explode('/', $this->path); $newSegments = array(); // Remove all unnecessary '.' and '..' segments foreach ($segments as $segment) { if ($segment == '..') { // Remove the previous part of the path $count = count($newSegments); if ($count > 0 && $newSegments[$count-1]) { array_pop($newSegments); } } elseif ($segment == '.') { // Ignore continue; } else { array_push($newSegments, $segment); } } // Construct the new normalised path $this->path = implode('/', $newSegments); // Allow easy chaining of methods return $this; } /** * Resolves a relative URI using this URI as the base URI. */ public function resolve($relUri) { // If it is a string, then convert it to a parsed object if (is_string($relUri)) { $relUri = new self($relUri); } // This code is based on the pseudocode in section 5.2.2 of RFC3986 $target = new self(); if ($relUri->scheme) { $target->scheme = $relUri->scheme; $target->authority = $relUri->authority; $target->path = $relUri->path; $target->query = $relUri->query; } else { if ($relUri->authority) { $target->authority = $relUri->authority; $target->path = $relUri->path; $target->query = $relUri->query; } else { if (empty($relUri->path)) { $target->path = $this->path; if ($relUri->query) { $target->query = $relUri->query; } else { $target->query = $this->query; } } else { if (substr($relUri->path, 0, 1) == '/') { $target->path = $relUri->path; } else { $path = $this->path; $lastSlash = strrpos($path, '/'); if ($lastSlash !== false) { $path = substr($path, 0, $lastSlash + 1); } else { $path = '/'; } $target->path .= $path . $relUri->path; } $target->query = $relUri->query; } $target->authority = $this->authority; } $target->scheme = $this->scheme; } $target->fragment = $relUri->fragment; $target->normalise(); return $target; } /** Convert the parsed URI back into a string * * @return string The URI as a string */ public function toString() { $str = ''; if ($this->scheme !== null) { $str .= $this->scheme . ':'; } if ($this->authority !== null) { $str .= '//' . $this->authority; } $str .= $this->path; if ($this->query !== null) { $str .= '?' . $this->query; } if ($this->fragment !== null) { $str .= '#' . $this->fragment; } return $str; } /** Magic method to convert the URI, when casted, back to a string * * @return string The URI as a string */ public function __toString() { return $this->toString(); } } easyrdf-1.0.0/lib/Parser.php000066400000000000000000000116371370344162000156700ustar00rootroot00000000000000bnodeMap[$name])) { $this->bnodeMap[$name] = $this->graph->newBNodeId(); } return $this->bnodeMap[$name]; } /** * Delete the bnode mapping - to be called at the start of a new parse * @ignore */ protected function resetBnodeMap() { $this->bnodeMap = array(); } /** * Check, cleanup parameters and prepare for parsing * @ignore */ protected function checkParseParams($graph, $data, $format, $baseUri) { if ($graph == null or !is_object($graph) or !($graph instanceof Graph)) { throw new \InvalidArgumentException( '$graph should be an EasyRdf\Graph object and cannot be null' ); } else { $this->graph = $graph; } if ($format == null or $format == '') { throw new \InvalidArgumentException( "\$format cannot be null or empty" ); } elseif (is_object($format) and $format instanceof Format) { $this->format = $format = $format->getName(); } elseif (!is_string($format)) { throw new \InvalidArgumentException( '$format should be a string or an EasyRdf\Format object' ); } else { $this->format = $format; } if ($baseUri) { if (!is_string($baseUri)) { throw new \InvalidArgumentException( "\$baseUri should be a string" ); } else { $this->baseUri = new ParsedUri($baseUri); } } else { $this->baseUri = null; } // Prepare for parsing $this->resetBnodeMap(); $this->tripleCount = 0; } /** * Sub-classes must follow this protocol * @ignore */ public function parse($graph, $data, $format, $baseUri) { throw new Exception( "This method should be overridden by sub-classes." ); } /** * Add a triple to the current graph, and keep count of the number of triples * @ignore */ protected function addTriple($resource, $property, $value) { $count = $this->graph->add($resource, $property, $value); $this->tripleCount += $count; return $count; } } easyrdf-1.0.0/lib/Parser/000077500000000000000000000000001370344162000151475ustar00rootroot00000000000000easyrdf-1.0.0/lib/Parser/Arc.php000066400000000000000000000070261370344162000163720ustar00rootroot00000000000000 'RDFXML', 'turtle' => 'Turtle', 'ntriples' => 'Turtle', 'rdfa' => 'SemHTML', ); /** * Constructor */ public function __construct() { if (!class_exists('ARC2')) { throw new \EasyRdf\Exception('ARC2 dependency is not installed'); } } /** * Parse an RDF document into an EasyRdf\Graph * * @param Graph $graph the graph to load the data into * @param string $data the RDF document data * @param string $format the format of the input data * @param string $baseUri the base URI of the data being parsed * * @throws \EasyRdf\Exception * @return integer The number of triples added to the graph */ public function parse($graph, $data, $format, $baseUri) { parent::checkParseParams($graph, $data, $format, $baseUri); if (array_key_exists($format, self::$supportedTypes)) { $className = self::$supportedTypes[$format]; } else { throw new \EasyRdf\Exception( "EasyRdf\\Parser\\Arc does not support: {$format}" ); } $parser = \ARC2::getParser($className); if ($parser) { $parser->parse($baseUri, $data); $rdfphp = $parser->getSimpleIndex(false); return parent::parse($graph, $rdfphp, 'php', $baseUri); } else { throw new \EasyRdf\Exception( "ARC2 failed to get a $className parser." ); } } } easyrdf-1.0.0/lib/Parser/Exception.php000066400000000000000000000051651370344162000176250ustar00rootroot00000000000000parserLine = $line; $this->parserColumn = $column; if (!is_null($line)) { $message .= " on line $line"; if (!is_null($column)) { $message .= ", column $column"; } } parent::__construct($message); } public function getParserLine() { return $this->parserLine; } public function getParserColumn() { return $this->parserColumn; } } easyrdf-1.0.0/lib/Parser/Json.php000066400000000000000000000127371370344162000166030ustar00rootroot00000000000000jsonLastErrorExists = function_exists('json_last_error'); } /** Return the last JSON parser error as a string * * If json_last_error() is not available a generic message will be returned. * * @ignore */ protected function jsonLastErrorString() { if ($this->jsonLastErrorExists) { switch (json_last_error()) { case JSON_ERROR_NONE: return null; case JSON_ERROR_DEPTH: return "JSON Parse error: the maximum stack depth has been exceeded"; case JSON_ERROR_STATE_MISMATCH: return "JSON Parse error: invalid or malformed JSON"; case JSON_ERROR_CTRL_CHAR: return "JSON Parse error: control character error, possibly incorrectly encoded"; case JSON_ERROR_SYNTAX: return "JSON Parse syntax error"; case JSON_ERROR_UTF8: return "JSON Parse error: malformed UTF-8 characters, possibly incorrectly encoded"; default: return "JSON Parse error: unknown"; } } else { return "JSON Parse error"; } } /** Parse the triple-centric JSON format, as output by libraptor * * http://librdf.org/raptor/api/serializer-json.html * * @ignore */ protected function parseJsonTriples($data, $baseUri) { foreach ($data['triples'] as $triple) { if ($triple['subject']['type'] == 'bnode') { $subject = $this->remapBnode($triple['subject']['value']); } else { $subject = $triple['subject']['value']; } $predicate = $triple['predicate']['value']; if ($triple['object']['type'] == 'bnode') { $object = array( 'type' => 'bnode', 'value' => $this->remapBnode($triple['object']['value']) ); } else { $object = $triple['object']; } $this->addTriple($subject, $predicate, $object); } return $this->tripleCount; } /** * Parse RDF/JSON into an EasyRdf\Graph * * @param Graph $graph the graph to load the data into * @param string $data the RDF document data * @param string $format the format of the input data * @param string $baseUri the base URI of the data being parsed * * @throws Exception * @throws \EasyRdf\Exception * @return integer The number of triples added to the graph */ public function parse($graph, $data, $format, $baseUri) { $this->checkParseParams($graph, $data, $format, $baseUri); if ($format != 'json') { throw new \EasyRdf\Exception( "EasyRdf\\Parser\\Json does not support: {$format}" ); } $decoded = @json_decode(strval($data), true); if ($decoded === null) { throw new Exception( $this->jsonLastErrorString() ); } if (array_key_exists('triples', $decoded)) { return $this->parseJsonTriples($decoded, $baseUri); } else { return parent::parse($graph, $decoded, 'php', $baseUri); } } } easyrdf-1.0.0/lib/Parser/JsonLd.php000066400000000000000000000111701370344162000170510ustar00rootroot00000000000000 * @license https://www.opensource.org/licenses/bsd-license.php */ class JsonLd extends Parser { /** * Parse a JSON-LD document into an EasyRdf\Graph * * Attention: Since JSON-LD supports datasets, a document may contain * multiple graphs and not just one. This parser returns only the * default graph. An alternative would be to merge all graphs. * * @param Graph $graph the graph to load the data into * @param string $data the RDF document data * @param string $format the format of the input data * @param string $baseUri the base URI of the data being parsed * * @throws Exception * @throws \EasyRdf\Exception * @return integer The number of triples added to the graph */ public function parse($graph, $data, $format, $baseUri) { parent::checkParseParams($graph, $data, $format, $baseUri); if ($format != 'jsonld') { throw new \EasyRdf\Exception( "EasyRdf\\Parser\\JsonLd does not support {$format}" ); } try { $quads = LD\JsonLD::toRdf($data, array('base' => $baseUri)); } catch (LD\Exception\JsonLdException $e) { throw new Exception($e->getMessage()); } foreach ($quads as $quad) { // Ignore named graphs if (null !== $quad->getGraph()) { continue; } $subject = (string) $quad->getSubject(); if ('_:' === substr($subject, 0, 2)) { $subject = $this->remapBnode($subject); } $predicate = (string) $quad->getProperty(); if ($quad->getObject() instanceof \ML\IRI\IRI) { $object = array( 'type' => 'uri', 'value' => (string) $quad->getObject() ); if ('_:' === substr($object['value'], 0, 2)) { $object = array( 'type' => 'bnode', 'value' => $this->remapBnode($object['value']) ); } } else { $object = array( 'type' => 'literal', 'value' => $quad->getObject()->getValue() ); if ($quad->getObject() instanceof LD\LanguageTaggedString) { $object['lang'] = $quad->getObject()->getLanguage(); } else { $object['datatype'] = $quad->getObject()->getType(); } } $this->addTriple($subject, $predicate, $object); } return $this->tripleCount; } } easyrdf-1.0.0/lib/Parser/Ntriples.php000066400000000000000000000174471370344162000174750ustar00rootroot00000000000000 chr(0x09), 'b' => chr(0x08), 'n' => chr(0x0A), 'r' => chr(0x0D), 'f' => chr(0x0C), '\"' => chr(0x22), '\'' => chr(0x27) ); foreach ($mappings as $in => $out) { $str = preg_replace('/\x5c([' . $in . '])/', $out, $str); } if (stripos($str, '\u') === false) { return $str; } while (preg_match('/\\\(U)([0-9A-F]{8})/', $str, $matches) || preg_match('/\\\(u)([0-9A-F]{4})/', $str, $matches)) { $no = hexdec($matches[2]); if ($no < 128) { // 0x80 $char = chr($no); } elseif ($no < 2048) { // 0x800 $char = chr(($no >> 6) + 192) . chr(($no & 63) + 128); } elseif ($no < 65536) { // 0x10000 $char = chr(($no >> 12) + 224) . chr((($no >> 6) & 63) + 128) . chr(($no & 63) + 128); } elseif ($no < 2097152) { // 0x200000 $char = chr(($no >> 18) + 240) . chr((($no >> 12) & 63) + 128) . chr((($no >> 6) & 63) + 128) . chr(($no & 63) + 128); } else { # FIXME: throw an exception instead? $char = ''; } $str = str_replace('\\' . $matches[1] . $matches[2], $char, $str); } return $str; } /** * @ignore */ protected function parseNtriplesSubject($sub, $lineNum) { if (preg_match('/<([^<>]+)>/', $sub, $matches)) { return $this->unescapeString($matches[1]); } elseif (preg_match('/_:([A-Za-z0-9]*)/', $sub, $matches)) { if (empty($matches[1])) { return $this->graph->newBNodeId(); } else { $nodeid = $this->unescapeString($matches[1]); return $this->remapBnode($nodeid); } } else { throw new Exception( "Failed to parse subject: $sub", $lineNum ); } } /** * @ignore */ protected function parseNtriplesObject($obj, $lineNum) { if (preg_match('/"(.+)"\^\^<([^<>]+)>/', $obj, $matches)) { return array( 'type' => 'literal', 'value' => $this->unescapeString($matches[1]), 'datatype' => $this->unescapeString($matches[2]) ); } elseif (preg_match('/"(.+)"@([\w\-]+)/', $obj, $matches)) { return array( 'type' => 'literal', 'value' => $this->unescapeString($matches[1]), 'lang' => $this->unescapeString($matches[2]) ); } elseif (preg_match('/"(.*)"/', $obj, $matches)) { return array('type' => 'literal', 'value' => $this->unescapeString($matches[1])); } elseif (preg_match('/<([^<>]+)>/', $obj, $matches)) { return array('type' => 'uri', 'value' => $this->unescapeString($matches[1])); } elseif (preg_match('/_:([A-Za-z0-9]*)/', $obj, $matches)) { if (empty($matches[1])) { return array( 'type' => 'bnode', 'value' => $this->graph->newBNodeId() ); } else { $nodeid = $this->unescapeString($matches[1]); return array( 'type' => 'bnode', 'value' => $this->remapBnode($nodeid) ); } } else { throw new Exception( "Failed to parse object: $obj", $lineNum ); } } /** * Parse an N-Triples document into an EasyRdf\Graph * * @param Graph $graph the graph to load the data into * @param string $data the RDF document data * @param string $format the format of the input data * @param string $baseUri the base URI of the data being parsed * * @throws Exception * @throws \EasyRdf\Exception * @return integer The number of triples added to the graph */ public function parse($graph, $data, $format, $baseUri) { parent::checkParseParams($graph, $data, $format, $baseUri); if ($format != 'ntriples') { throw new \EasyRdf\Exception( "EasyRdf\\Parser\\Ntriples does not support: $format" ); } $lines = preg_split('/\x0D?\x0A/', strval($data)); foreach ($lines as $index => $line) { $lineNum = $index + 1; if (preg_match('/^\s*#/', $line)) { # Comment continue; } elseif (preg_match('/^\s*(.+?)\s+<([^<>]+?)>\s+(.+?)\s*\.\s*$/', $line, $matches)) { $this->addTriple( $this->parseNtriplesSubject($matches[1], $lineNum), $this->unescapeString($matches[2]), $this->parseNtriplesObject($matches[3], $lineNum) ); } elseif (preg_match('/^\s*$/', $line)) { # Blank line continue; } else { throw new Exception( "Failed to parse statement", $lineNum ); } } return $this->tripleCount; } } easyrdf-1.0.0/lib/Parser/Rapper.php000066400000000000000000000074601370344162000171200ustar00rootroot00000000000000/dev/null", $output, $status); if ($status != 0) { throw new \EasyRdf\Exception( "Failed to execute the command '$rapperCmd': " . join("\n", $output) ); } elseif (version_compare($output[0], self::MINIMUM_RAPPER_VERSION) < 0) { throw new \EasyRdf\Exception( "Version ".self::MINIMUM_RAPPER_VERSION." or higher of rapper is required." ); } else { $this->rapperCmd = $rapperCmd; } } /** * Parse an RDF document into an EasyRdf\Graph * * @param Graph $graph the graph to load the data into * @param string $data the RDF document data * @param string $format the format of the input data * @param string $baseUri the base URI of the data being parsed * * @return integer The number of triples added to the graph */ public function parse($graph, $data, $format, $baseUri) { parent::checkParseParams($graph, $data, $format, $baseUri); $json = Utils::execCommandPipe( $this->rapperCmd, array( '--quiet', '--input', $format, '--output', 'json', '--ignore-errors', '--input-uri', $baseUri, '--output-uri', '-', '-' ), $data ); // Parse in the JSON return parent::parse($graph, $json, 'json', $baseUri); } } easyrdf-1.0.0/lib/Parser/RdfPhp.php000066400000000000000000000120231370344162000170410ustar00rootroot00000000000000checkParseParams($graph, $data, $format, $baseUri); if ($format != 'php') { throw new \EasyRdf\Exception( "EasyRdf\\Parser\\RdfPhp does not support: $format" ); } if (!is_array($data)) { throw new Exception('expected array, got '.gettype($data)); } foreach ($data as $orig_subject => $properties) { if (is_int($orig_subject)) { throw new Exception('expected array indexed by IRIs, got list'); } if (substr($orig_subject, 0, 2) === '_:') { $subject = $this->remapBnode($orig_subject); } elseif (preg_match('/^\w+$/', $orig_subject)) { # Cope with invalid RDF/JSON serialisations that # put the node name in, without the _: prefix # (such as net.fortytwo.sesametools.rdfjson) $subject = $this->remapBnode($orig_subject); } else { $subject = $orig_subject; } if (!is_array($properties)) { throw new Exception("expected array as value of '{$orig_subject}' key, got ".gettype($properties)); } foreach ($properties as $property => $objects) { if (is_int($property)) { throw new Exception("expected 'array indexed by IRIs' as value of '{$orig_subject}' key, got list"); } if (!is_array($objects)) { throw new Exception( "expected list of objects as value of '{$orig_subject}' -> '{$property}' node, got ". gettype($objects) ); } foreach ($objects as $i => $object) { if (!is_array($object) or !isset($object['type']) or !isset($object['value'])) { throw new Exception( "expected array with 'type' and 'value' keys as value of ". "'{$orig_subject}' -> '{$property}' -> '{$i}' node" ); } if ($object['type'] === 'bnode') { $object['value'] = $this->remapBnode($object['value']); } $this->addTriple($subject, $property, $object); } } } return $this->tripleCount; } } easyrdf-1.0.0/lib/Parser/RdfXml.php000066400000000000000000000660321370344162000170630ustar00rootroot00000000000000graph = $graph; $this->state = 0; $this->xLang = null; $this->xBase = new ParsedUri($base); $this->xml = 'http://www.w3.org/XML/1998/namespace'; $this->rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; $this->nsp = array($this->xml => 'xml', $this->rdf => 'rdf'); $this->sStack = array(); $this->sCount = 0; } /** @ignore */ protected function initXMLParser() { if (!isset($this->xmlParser)) { $parser = xml_parser_create_ns('UTF-8', ''); xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); xml_set_element_handler($parser, 'startElementHandler', 'endElementHandler'); xml_set_character_data_handler($parser, 'cdataHandler'); xml_set_start_namespace_decl_handler($parser, 'newNamespaceHandler'); xml_set_object($parser, $this); $this->xmlParser = $parser; } } /** @ignore */ protected function pushS(&$s) { $s['pos'] = $this->sCount; $this->sStack[$this->sCount] = $s; $this->sCount++; } /** @ignore */ protected function popS() { $r = array(); $this->sCount--; for ($i = 0, $iMax = $this->sCount; $i < $iMax; $i++) { $r[$i] = $this->sStack[$i]; } $this->sStack = $r; } /** @ignore */ protected function updateS($s) { $this->sStack[$s['pos']] = $s; } /** @ignore */ protected function getParentS() { if ($this->sCount && isset($this->sStack[$this->sCount - 1])) { return $this->sStack[$this->sCount - 1]; } else { return false; } } /** @ignore */ protected function getParentXBase() { if ($p = $this->getParentS()) { if (isset($p['p_x_base']) && $p['p_x_base']) { return $p['p_x_base']; } elseif (isset($p['x_base'])) { return $p['x_base']; } else { return ''; } } else { return $this->xBase; } } /** @ignore */ protected function getParentXLang() { if ($p = $this->getParentS()) { if (isset($p['p_x_lang']) && $p['p_x_lang']) { return $p['p_x_lang']; } elseif (isset($p['x_lang'])) { return $p['x_lang']; } else { return null; } } else { return $this->xLang; } } /** @ignore */ protected function splitURI($v) { /* auto-splitting on / or # */ if (preg_match('/^(.*[\/\#])([^\/\#]+)$/', $v, $m)) { return array($m[1], $m[2]); } /* auto-splitting on last special char, e.g. urn:foo:bar */ if (preg_match('/^(.*[\:\/])([^\:\/]+)$/', $v, $m)) { return array($m[1], $m[2]); } return array($v, ''); } /** @ignore */ protected function add($s, $p, $o, $sType, $oType, $oDatatype = null, $oLang = null) { $this->addTriple( $s, $p, array( 'type' => $oType, 'value' => $o, 'lang' => $oLang, 'datatype' => $oDatatype ) ); } /** @ignore */ protected function reify($t, $s, $p, $o, $sType, $oType, $oDatatype = null, $oLang = null) { $this->add($t, $this->rdf.'type', $this->rdf.'Statement', 'uri', 'uri'); $this->add($t, $this->rdf.'subject', $s, 'uri', $sType); $this->add($t, $this->rdf.'predicate', $p, 'uri', 'uri'); $this->add($t, $this->rdf.'object', $o, 'uri', $oType, $oDatatype, $oLang); } /** @ignore */ protected function startElementHandler($p, $t, $a) { switch ($this->state) { case 0: return $this->startState0($t, $a); case 1: return $this->startState1($t, $a); case 2: return $this->startState2($t, $a); case 4: return $this->startState4($t, $a); case 5: return $this->startState5($t, $a); case 6: return $this->startState6($t, $a); default: throw new Exception( 'startElementHandler() called at state ' . $this->state . ' in '.$t ); } } /** @ignore */ protected function endElementHandler($p, $t) { switch ($this->state) { case 1: return $this->endState1($t); case 2: return $this->endState2($t); case 3: return $this->endState3($t); case 4: return $this->endState4($t); case 5: return $this->endState5($t); case 6: return $this->endState6($t); default: throw new Exception( 'endElementHandler() called at state ' . $this->state . ' in '.$t ); } } /** @ignore */ protected function cdataHandler($p, $d) { switch ($this->state) { case 4: return $this->cdataState4($d); case 6: return $this->cdataState6($d); default: return false; } } /** @ignore */ protected function newNamespaceHandler($p, $prf, $uri) { $this->nsp[$uri] = isset($this->nsp[$uri]) ? $this->nsp[$uri] : $prf; } /** @ignore */ protected function startState0($t, $a) { $this->state = 1; if ($t !== $this->rdf.'RDF') { $this->startState1($t, $a); } else { if (isset($a[$this->xml.'base'])) { $this->xBase = $this->xBase->resolve($a[$this->xml.'base']); } } } /** @ignore */ protected function startState1($t, $a) { $s = array( 'x_base' => $this->getParentXBase(), 'x_lang' => $this->getParentXLang(), 'li_count' => 0, ); if (isset($a[$this->xml.'base'])) { $s['x_base'] = $this->xBase->resolve($a[$this->xml.'base']); } if (isset($a[$this->xml.'lang'])) { $s['x_lang'] = $a[$this->xml.'lang']; } /* ID */ if (isset($a[$this->rdf.'ID'])) { $s['type'] = 'uri'; $s['value'] = $s['x_base']->resolve('#'.$a[$this->rdf.'ID']); /* about */ } elseif (isset($a[$this->rdf.'about'])) { $s['type'] = 'uri'; $s['value'] = $s['x_base']->resolve($a[$this->rdf.'about']); /* bnode */ } else { $s['type'] = 'bnode'; if (isset($a[$this->rdf.'nodeID'])) { $s['value'] = $this->remapBnode($a[$this->rdf.'nodeID']); } else { $s['value'] = $this->graph->newBNodeId(); } } /* sub-node */ if ($this->state === 4) { $supS = $this->getParentS(); /* new collection */ if (isset($supS['o_is_coll']) && $supS['o_is_coll']) { $coll = array( 'type' => 'bnode', 'value' => $this->graph->newBNodeId(), 'is_coll' => true, 'x_base' => $s['x_base'], 'x_lang' => $s['x_lang'] ); $this->add($supS['value'], $supS['p'], $coll['value'], $supS['type'], $coll['type']); $this->add($coll['value'], $this->rdf.'first', $s['value'], $coll['type'], $s['type']); $this->pushS($coll); } elseif (isset($supS['is_coll']) && $supS['is_coll']) { /* new entry in existing coll */ $coll = array( 'type' => 'bnode', 'value' => $this->graph->newBNodeId(), 'is_coll' => true, 'x_base' => $s['x_base'], 'x_lang' => $s['x_lang'] ); $this->add($supS['value'], $this->rdf.'rest', $coll['value'], $supS['type'], $coll['type']); $this->add($coll['value'], $this->rdf.'first', $s['value'], $coll['type'], $s['type']); $this->pushS($coll); /* normal sub-node */ } elseif (isset($supS['p']) && $supS['p']) { $this->add($supS['value'], $supS['p'], $s['value'], $supS['type'], $s['type']); } } /* typed node */ if ($t !== $this->rdf.'Description') { $this->add($s['value'], $this->rdf.'type', $t, $s['type'], 'uri'); } /* (additional) typing attr */ if (isset($a[$this->rdf.'type'])) { $this->add($s['value'], $this->rdf.'type', $a[$this->rdf.'type'], $s['type'], 'uri'); } /* Seq|Bag|Alt */ // if (in_array($t, array($this->rdf.'Seq', $this->rdf.'Bag', $this->rdf.'Alt'))) { // # FIXME: what is this? // $s['is_con'] = true; // } /* any other attrs (skip rdf and xml, except rdf:_, rdf:value, rdf:Seq) */ foreach ($a as $k => $v) { if (((strpos($k, $this->xml) === false) && (strpos($k, $this->rdf) === false)) || preg_match('/(\_[0-9]+|value|Seq|Bag|Alt|Statement|Property|List)$/', $k)) { if (strpos($k, ':')) { $this->add($s['value'], $k, $v, $s['type'], 'literal', null, $s['x_lang']); } } } $this->pushS($s); $this->state = 2; } /** @ignore */ protected function startState2($t, $a) { $s = $this->getParentS(); foreach (array('p_x_base', 'p_x_lang', 'p_id', 'o_is_coll') as $k) { unset($s[$k]); } /* base */ if (isset($a[$this->xml.'base'])) { $s['p_x_base'] = $s['x_base']->resolve($a[$this->xml.'base']); } $b = isset($s['p_x_base']) && $s['p_x_base'] ? $s['p_x_base'] : $s['x_base']; /* lang */ if (isset($a[$this->xml.'lang'])) { $s['p_x_lang'] = $a[$this->xml.'lang']; } $l = isset($s['p_x_lang']) && $s['p_x_lang'] ? $s['p_x_lang'] : $s['x_lang']; /* adjust li */ if ($t === $this->rdf.'li') { $s['li_count']++; $t = $this->rdf.'_'.$s['li_count']; } /* set p */ $s['p'] = $t; /* reification */ if (isset($a[$this->rdf.'ID'])) { $s['p_id'] = $a[$this->rdf.'ID']; } $o = array('value' => null, 'type' => null, 'x_base' => $b, 'x_lang' => $l); /* resource/rdf:resource */ if (isset($a['resource'])) { $a[$this->rdf.'resource'] = $a['resource']; unset($a['resource']); } if (isset($a[$this->rdf.'resource'])) { $o['type'] = 'uri'; $o['value'] = $b->resolve($a[$this->rdf.'resource']); $this->add($s['value'], $s['p'], $o['value'], $s['type'], $o['type']); /* type */ if (isset($a[$this->rdf.'type'])) { $this->add( $o['value'], $this->rdf.'type', $a[$this->rdf.'type'], 'uri', 'uri' ); } /* reification */ if (isset($s['p_id'])) { $this->reify( $b->resolve('#'.$s['p_id']), $s['value'], $s['p'], $o['value'], $s['type'], $o['type'] ); unset($s['p_id']); } $this->state = 3; } elseif (isset($a[$this->rdf.'nodeID'])) { /* named bnode */ $o['value'] = $this->remapBnode($a[$this->rdf.'nodeID']); $o['type'] = 'bnode'; $this->add($s['value'], $s['p'], $o['value'], $s['type'], $o['type']); $this->state = 3; /* reification */ if (isset($s['p_id'])) { $this->reify( $b->resolve('#'.$s['p_id']), $s['value'], $s['p'], $o['value'], $s['type'], $o['type'] ); } /* parseType */ } elseif (isset($a[$this->rdf.'parseType'])) { if ($a[$this->rdf.'parseType'] === 'Literal') { $s['o_xml_level'] = 0; $s['o_xml_data'] = ''; $s['p_xml_literal_level'] = 0; $s['ns'] = array(); $this->state = 6; } elseif ($a[$this->rdf.'parseType'] === 'Resource') { $o['value'] = $this->graph->newBNodeId(); $o['type'] = 'bnode'; $o['hasClosingTag'] = 0; $this->add($s['value'], $s['p'], $o['value'], $s['type'], $o['type']); $this->pushS($o); /* reification */ if (isset($s['p_id'])) { $this->reify( $b->resolve('#'.$s['p_id']), $s['value'], $s['p'], $o['value'], $s['type'], $o['type'] ); unset($s['p_id']); } $this->state = 2; } elseif ($a[$this->rdf.'parseType'] === 'Collection') { $s['o_is_coll'] = true; $this->state = 4; } } else { /* sub-node or literal */ $s['o_cdata'] = ''; if (isset($a[$this->rdf.'datatype'])) { $s['o_datatype'] = $a[$this->rdf.'datatype']; } $this->state = 4; } /* any other attrs (skip rdf and xml) */ foreach ($a as $k => $v) { if (((strpos($k, $this->xml) === false) && (strpos($k, $this->rdf) === false)) || preg_match('/(\_[0-9]+|value)$/', $k)) { if (strpos($k, ':')) { if (!$o['value']) { $o['value'] = $this->graph->newBNodeId(); $o['type'] = 'bnode'; $this->add($s['value'], $s['p'], $o['value'], $s['type'], $o['type']); } /* reification */ if (isset($s['p_id'])) { $this->reify( $b->resolve('#'.$s['p_id']), $s['value'], $s['p'], $o['value'], $s['type'], $o['type'] ); unset($s['p_id']); } $this->add($o['value'], $k, $v, $o['type'], 'literal'); $this->state = 3; } } } $this->updateS($s); } /** @ignore */ protected function startState4($t, $a) { return $this->startState1($t, $a); } /** @ignore */ protected function startState5($t, $a) { $this->state = 4; return $this->startState4($t, $a); } /** @ignore */ protected function startState6($t, $a) { $s = $this->getParentS(); $data = isset($s['o_xml_data']) ? $s['o_xml_data'] : ''; $ns = isset($s['ns']) ? $s['ns'] : array(); $parts = $this->splitURI($t); if (count($parts) === 1) { $data .= '<'.$t; } else { $nsUri = $parts[0]; $name = $parts[1]; if (!isset($this->nsp[$nsUri])) { foreach ($this->nsp as $tmp1 => $tmp2) { if (strpos($t, $tmp1) === 0) { $nsUri = $tmp1; $name = substr($t, strlen($tmp1)); break; } } } $nsp = isset($this->nsp[$nsUri]) ? $this->nsp[$nsUri] : ''; $data .= $nsp ? '<' . $nsp . ':' . $name : '<' . $name; /* ns */ if (!isset($ns[$nsp.'='.$nsUri]) || !$ns[$nsp.'='.$nsUri]) { $data .= $nsp ? ' xmlns:'.$nsp.'="'.$nsUri.'"' : ' xmlns="'.$nsUri.'"'; $ns[$nsp.'='.$nsUri] = true; $s['ns'] = $ns; } } foreach ($a as $k => $v) { $parts = $this->splitURI($k); if (count($parts) === 1) { $data .= ' '.$k.'="'.$v.'"'; } else { $nsUri = $parts[0]; $name = $parts[1]; $nsp = isset($this->nsp[$nsUri]) ? $this->nsp[$nsUri] : ''; $data .= $nsp ? ' '.$nsp.':'.$name.'="'.$v.'"' : ' '.$name.'="'.$v.'"' ; } } $data .= '>'; $s['o_xml_data'] = $data; $s['o_xml_level'] = isset($s['o_xml_level']) ? $s['o_xml_level'] + 1 : 1; if ($t == $s['p']) {/* xml container prop */ $s['p_xml_literal_level'] = isset($s['p_xml_literal_level']) ? $s['p_xml_literal_level'] + 1 : 1; } $this->updateS($s); } /** @ignore */ protected function endState1($t) { /* end of doc */ $this->state = 0; } /** @ignore */ protected function endState2($t) { /* expecting a prop, getting a close */ if ($s = $this->getParentS()) { $hasClosingTag = (isset($s['hasClosingTag']) && !$s['hasClosingTag']) ? 0 : 1; $this->popS(); $this->state = 5; if ($s = $this->getParentS()) { /* new s */ if (!isset($s['p']) || !$s['p']) { /* p close after collection|parseType=Resource|node close after p close */ $this->state = $this->sCount ? 4 : 1; if (!$hasClosingTag) { $this->state = 2; } } elseif (!$hasClosingTag) { $this->state = 2; } } } } /** @ignore */ protected function endState3($t) { /* p close */ $this->state = 2; } /** @ignore */ protected function endState4($t) { /* empty p | pClose after cdata | pClose after collection */ if ($s = $this->getParentS()) { $b = isset($s['p_x_base']) && $s['p_x_base'] ? $s['p_x_base'] : (isset($s['x_base']) ? $s['x_base'] : ''); if (isset($s['is_coll']) && $s['is_coll']) { $this->add($s['value'], $this->rdf.'rest', $this->rdf.'nil', $s['type'], 'uri'); /* back to collection start */ while ((!isset($s['p']) || ($s['p'] != $t))) { $subS = $s; $this->popS(); $s = $this->getParentS(); } /* reification */ if (isset($s['p_id']) && $s['p_id']) { $this->reify( $b->resolve('#'.$s['p_id']), $s['value'], $s['p'], $subS['value'], $s['type'], $subS['type'] ); } unset($s['p']); $this->updateS($s); } else { $dt = isset($s['o_datatype']) ? $s['o_datatype'] : null; $l = isset($s['p_x_lang']) && $s['p_x_lang'] ? $s['p_x_lang'] : (isset($s['x_lang']) ? $s['x_lang'] : null); $o = array('type' => 'literal', 'value' => $s['o_cdata']); $this->add( $s['value'], $s['p'], $o['value'], $s['type'], $o['type'], $dt, $l ); /* reification */ if (isset($s['p_id']) && $s['p_id']) { $this->reify( $b->resolve('#'.$s['p_id']), $s['value'], $s['p'], $o['value'], $s['type'], $o['type'], $dt, $l ); } unset($s['o_cdata']); unset($s['o_datatype']); unset($s['p']); $this->updateS($s); } $this->state = 2; } } /** @ignore */ protected function endState5($t) { /* p close */ if ($s = $this->getParentS()) { unset($s['p']); $this->updateS($s); $this->state = 2; } } /** @ignore */ protected function endState6($t) { if ($s = $this->getParentS()) { $l = isset($s['p_x_lang']) && $s['p_x_lang'] ? $s['p_x_lang'] : (isset($s['x_lang']) ? $s['x_lang'] : null); $data = $s['o_xml_data']; $level = $s['o_xml_level']; if ($level === 0) { /* pClose */ $this->add( $s['value'], $s['p'], trim($data, ' '), $s['type'], 'literal', $this->rdf.'XMLLiteral', $l ); unset($s['o_xml_data']); $this->state = 2; } else { $parts = $this->splitURI($t); if (count($parts) == 1) { $data .= ''; } else { $nsUri = $parts[0]; $name = $parts[1]; if (!isset($this->nsp[$nsUri])) { foreach ($this->nsp as $tmp1 => $tmp2) { if (strpos($t, $tmp1) === 0) { $nsUri = $tmp1; $name = substr($t, strlen($tmp1)); break; } } } $nsp = isset($this->nsp[$nsUri]) ? $this->nsp[$nsUri] : ''; $data .= $nsp ? '' : ''; } $s['o_xml_data'] = $data; $s['o_xml_level'] = $level - 1; if ($t == $s['p']) { /* xml container prop */ $s['p_xml_literal_level']--; } } $this->updateS($s); } } /** @ignore */ protected function cdataState4($d) { if ($s = $this->getParentS()) { $s['o_cdata'] = isset($s['o_cdata']) ? $s['o_cdata'] . $d : $d; $this->updateS($s); } } /** @ignore */ protected function cdataState6($d) { if ($s = $this->getParentS()) { if (isset($s['o_xml_data']) || preg_match('/[\n\r]/', $d) || trim($d)) { $d = htmlspecialchars($d, ENT_NOQUOTES); $s['o_xml_data'] = isset($s['o_xml_data']) ? $s['o_xml_data'] . $d : $d; } $this->updateS($s); } } /** * Parse an RDF/XML document into an EasyRdf\Graph * * @param Graph $graph the graph to load the data into * @param string $data the RDF document data * @param string $format the format of the input data * @param string $baseUri the base URI of the data being parsed * * @throws Exception * @throws \EasyRdf\Exception * @return integer The number of triples added to the graph */ public function parse($graph, $data, $format, $baseUri) { parent::checkParseParams($graph, $data, $format, $baseUri); if ($format != 'rdfxml') { throw new \EasyRdf\Exception( "EasyRdf\\Parser\\RdfXml does not support: $format" ); } $this->init($graph, $baseUri); $this->resetBnodeMap(); /* xml parser */ $this->initXMLParser(); /* parse */ if (!xml_parse($this->xmlParser, $data, false)) { $message = xml_error_string(xml_get_error_code($this->xmlParser)); throw new Exception( 'XML error: "' . $message . '"', xml_get_current_line_number($this->xmlParser), xml_get_current_column_number($this->xmlParser) ); } xml_parser_free($this->xmlParser); return $this->tripleCount; } } easyrdf-1.0.0/lib/Parser/Rdfa.php000066400000000000000000000657001370344162000165440ustar00rootroot00000000000000debug) { print "Adding triple: $resource -> $property -> ".$value['type'].':'.$value['value']."\n"; } $count = $this->graph->add($resource, $property, $value); $this->tripleCount += $count; return $count; } protected function generateList($subject, $property, $list) { $current = $subject; $prop = $property; // Output a blank node for each item in the list foreach ($list as $item) { $newNode = $this->graph->newBNodeId(); $this->addTriple($current, $prop, array('type' => 'bnode', 'value' => $newNode)); $this->addTriple($newNode, 'rdf:first', $item); $current = $newNode; $prop = 'rdf:rest'; } // Finally, terminate the list $this->addTriple( $current, $prop, array('type' => 'uri', 'value' => RdfNamespace::expand('rdf:nil')) ); } protected function addToList($listMapping, $property, $value) { if ($this->debug) { print "Adding to list: $property -> ".$value['type'].':'.$value['value']."\n"; } // Create property in the list mapping if it doesn't already exist if (!isset($listMapping->$property)) { $listMapping->$property = array(); } array_push($listMapping->$property, $value); } protected function printNode($node, $depth) { $indent = str_repeat(' ', $depth); print $indent; switch ($node->nodeType) { case XML_ELEMENT_NODE: print 'node'; break; case XML_ATTRIBUTE_NODE: print 'attr'; break; case XML_TEXT_NODE: print 'text'; break; case XML_CDATA_SECTION_NODE: print 'cdata'; break; case XML_ENTITY_REF_NODE: print 'entref'; break; case XML_ENTITY_NODE: print 'entity'; break; case XML_PI_NODE: print 'pi'; break; case XML_COMMENT_NODE: print 'comment'; break; case XML_DOCUMENT_NODE: print 'doc'; break; case XML_DOCUMENT_TYPE_NODE: print 'doctype'; break; case XML_HTML_DOCUMENT_NODE: print 'html'; break; default: throw new \EasyRdf\Exception("unknown node type: ".$node->nodeType); break; } print ' '.$node->nodeName."\n"; if ($node->hasAttributes()) { foreach ($node->attributes as $attr) { print $indent.' '.$attr->nodeName." => ".$attr->nodeValue."\n"; } } } protected function guessTimeDatatype($value) { if (preg_match('/^-?\d{4}-\d{2}-\d{2}(Z|[\-\+]\d{2}:\d{2})?$/', $value)) { return 'http://www.w3.org/2001/XMLSchema#date'; } elseif (preg_match('/^\d{2}:\d{2}:\d{2}(Z|[\-\+]\d{2}:\d{2})?$/', $value)) { return 'http://www.w3.org/2001/XMLSchema#time'; } elseif (preg_match('/^-?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[\-\+]\d{2}:\d{2})?$/', $value)) { return 'http://www.w3.org/2001/XMLSchema#dateTime'; } elseif (preg_match('/^P(\d+Y)?(\d+M)?(\d+D)?T?(\d+H)?(\d+M)?(\d+S)?$/', $value)) { return 'http://www.w3.org/2001/XMLSchema#duration'; } elseif (preg_match('/^\d{4}$/', $value)) { return 'http://www.w3.org/2001/XMLSchema#gYear'; } elseif (preg_match('/^\d{4}-\d{2}$/', $value)) { return 'http://www.w3.org/2001/XMLSchema#gYearMonth'; } else { return null; } } protected function initialContext() { $context = array( 'prefixes' => array(), 'vocab' => null, 'subject' => $this->baseUri, 'property' => null, 'object' => null, 'terms' => array(), 'incompleteRels' => array(), 'incompleteRevs' => array(), 'listMapping' => null, 'lang' => null, 'path' => '', 'xmlns' => array(), ); // Set the default prefix $context['prefixes'][''] = 'http://www.w3.org/1999/xhtml/vocab#'; // RDFa 1.1 default term mapping $context['terms']['describedby'] = 'http://www.w3.org/2007/05/powder-s#describedby'; $context['terms']['license'] = 'http://www.w3.org/1999/xhtml/vocab#license'; $context['terms']['role'] = 'http://www.w3.org/1999/xhtml/vocab#role'; return $context; } protected function expandCurie($node, &$context, $value) { if (preg_match('/^(\w*?):(.*)$/', $value, $matches)) { list (, $prefix, $local) = $matches; $prefix = strtolower($prefix); if ($prefix === '_') { // It is a bnode return $this->remapBnode(substr($value, 2)); } elseif (empty($prefix) and $context['vocab']) { // Empty prefix return $context['vocab'] . $local; } elseif (isset($context['prefixes'][$prefix])) { return $context['prefixes'][$prefix] . $local; } elseif ($uri = $node->lookupNamespaceURI($prefix)) { return $uri . $local; } elseif (!empty($prefix) and $uri = RdfNamespace::get($prefix)) { // Expand using well-known prefixes return $uri . $local; } } } protected function processUri($node, &$context, $value, $isProp = false) { if (preg_match('/^\[(.*)\]$/', $value, $matches)) { // Safe CURIE return $this->expandCurie($node, $context, $matches[1]); } elseif (preg_match(self::TERM_REGEXP, $value) and $isProp) { $term = strtolower($value); if ($context['vocab']) { return $context['vocab'] . $value; } elseif (isset($context['terms'][$term])) { return $context['terms'][$term]; } } elseif (substr($value, 0, 2) === '_:' and $isProp) { return null; } else { $uri = $this->expandCurie($node, $context, $value); if ($uri) { return $uri; } else { $parsed = new ParsedUri($value); if ($parsed->isAbsolute()) { return $value; } elseif ($isProp) { // Properties can't be relative URIs return null; } elseif ($this->baseUri) { return $this->baseUri->resolve($parsed); } } } } protected function processUriList($node, $context, $values) { if (!$values) { return array(); } $uris = array(); foreach (preg_split('/\s+/', $values) as $value) { $uri = $this->processUri($node, $context, $value, true); if ($uri) { array_push($uris, $uri); } } return $uris; } protected function getUriAttribute($node, &$context, $attributes) { if (!is_array($attributes)) { $attributes = array($attributes); } // Find the first attribute that returns a valid URI foreach ($attributes as $attribute) { if ($node->hasAttribute($attribute)) { $value = $node->getAttribute($attribute); $uri = $this->processUri($node, $context, $value); if ($uri) { return $uri; } } } } protected function processNode($node, &$context, $depth = 1) { if ($this->debug) { $this->printNode($node, $depth); } // Step 1: establish local variables $skip = false; $subject = null; $typedResource = null; $object = null; $lang = $context['lang']; $incompleteRels = array(); $incompleteRevs = array(); if ($node->nodeType === XML_ELEMENT_NODE) { $context['path'] .= '/' . $node->nodeName; $content = $node->hasAttribute('content') ? $node->getAttribute('content') : null; $datatype = $node->hasAttribute('datatype') ? $node->getAttribute('datatype') : null; $property = $node->getAttribute('property') ? $node->getAttribute('property') : null; $typeof = $node->getAttribute('typeof') ? $node->getAttribute('typeof') : null; // Step 2: Default vocabulary if ($node->hasAttribute('vocab')) { $context['vocab'] = $node->getAttribute('vocab'); if ($context['vocab']) { $this->addTriple( $this->baseUri, 'rdfa:usesVocabulary', array('type' => 'uri', 'value' => $context['vocab']) ); } } // Step 3: Set prefix mappings // Support for deprecated xmlns if present in document foreach ($context['xmlns'] as $prefix => $uri) { if ($node->hasAttribute('xmlns:' . $prefix)) { $context['prefixes'][$prefix] = $node->getAttribute('xmlns:' . $prefix); if ($this->debug) { print "Prefix (xmlns): $prefix => $uri\n"; } } } if ($node->hasAttribute('prefix')) { $mappings = preg_split('/\s+/', $node->getAttribute('prefix')); while (count($mappings)) { $prefix = strtolower(array_shift($mappings)); $uri = array_shift($mappings); if (substr($prefix, -1) === ':') { $prefix = substr($prefix, 0, -1); } else { continue; } if ($prefix === '_') { continue; } elseif (!empty($prefix)) { $context['prefixes'][$prefix] = $uri; if ($this->debug) { print "Prefix: $prefix => $uri\n"; } } } } // Step 4 if ($node->hasAttributeNS(self::XML_NS, 'lang')) { $lang = $node->getAttributeNS(self::XML_NS, 'lang'); } elseif ($node->hasAttribute('lang')) { $lang = $node->getAttribute('lang'); } // HTML+RDFa 1.1: ignore rel and rev unless they contain CURIEs. foreach (array('rel', 'rev') as $attr) { if ($node->hasAttribute('property') and $node->hasAttribute($attr)) { // Quick check in case there are no CURIEs to deal with. if (strpos($node->getAttribute($attr), ':') === false) { $node->removeAttribute($attr); } else { // Only keep CURIEs. $curies = array(); foreach (preg_split('/\s+/', $node->getAttribute($attr)) as $token) { if (strpos($token, ':')) { $curies[] = $token; } } $node->setAttribute($attr, implode(' ', $curies)); } } } $rels = $this->processUriList($node, $context, $node->getAttribute('rel')); $revs = $this->processUriList($node, $context, $node->getAttribute('rev')); if (!$node->hasAttribute('rel') and !$node->hasAttribute('rev')) { // Step 5: Establish a new subject if no rel/rev if ($property and is_null($content) and is_null($datatype)) { $subject = $this->getUriAttribute($node, $context, 'about'); if ($typeof and !$subject) { $typedResource = $this->getUriAttribute( $node, $context, array('resource', 'href', 'src') ); if (!$typedResource) { $typedResource = $this->graph->newBNodeId(); } $object = $typedResource; } } else { $subject = $this->getUriAttribute( $node, $context, array('about', 'resource', 'href', 'src') ); } // Establish a subject if there isn't one # FIXME: refactor this if (is_null($subject)) { if ($context['path'] === '/html/head') { $subject = $context['object']; } elseif ($depth <= 2) { $subject = $this->baseUri; } elseif ($typeof and !$property) { $subject = $this->graph->newBNodeId(); } else { if (!$property) { $skip = true; } $subject = $context['object']; } } } else { // Step 6 // If the current element does contain a @rel or @rev attribute, then the next step is to // establish both a value for new subject and a value for current object resource: $subject = $this->getUriAttribute($node, $context, 'about'); $object = $this->getUriAttribute( $node, $context, array('resource', 'href', 'src') ); if ($typeof) { if (!$object and !$subject) { $object = $this->graph->newBNodeId(); } $typedResource = $subject ? $subject : $object; } # FIXME: if the element is the root element of the document # then act as if there is an empty @about present if (!$subject) { $subject = $context['object']; } } # FIXME: better place for this? if ($typeof and $subject and !$typedResource) { $typedResource = $subject; } // Step 7: Process @typeof if there is a subject if ($typedResource) { foreach ($this->processUriList($node, $context, $typeof) as $type) { $this->addTriple( $typedResource, 'rdf:type', array('type' => 'uri', 'value' => $type) ); } } // Step 8: Create new List mapping if the subject has changed if ($subject and $subject !== $context['subject']) { $listMapping = new \stdClass(); } else { $listMapping = $context['listMapping']; } // Step 9: Generate triples with given object if ($subject and $object) { foreach ($rels as $prop) { $obj = array('type' => 'uri', 'value' => $object); if ($node->hasAttribute('inlist')) { $this->addToList($listMapping, $prop, $obj); } else { $this->addTriple($subject, $prop, $obj); } } foreach ($revs as $prop) { $this->addTriple( $object, $prop, array('type' => 'uri', 'value' => $subject) ); } } elseif ($rels or $revs) { // Step 10: Incomplete triples and bnode creation $object = $this->graph->newBNodeId(); if ($rels) { if ($node->hasAttribute('inlist')) { foreach ($rels as $prop) { # FIXME: add support for incomplete lists if (!isset($listMapping->$prop)) { $listMapping->$prop = array(); } } } else { $incompleteRels = $rels; if ($this->debug) { print "Incomplete rels: ".implode(',', $rels)."\n"; } } } if ($revs) { $incompleteRevs = $revs; if ($this->debug) { print "Incomplete revs: ".implode(',', $revs)."\n"; } } } // Step 11: establish current property value if ($subject and $property) { $value = array(); if ($datatype) { $datatype = $this->processUri($node, $context, $datatype, true); } if ($content !== null) { $value['value'] = $content; } elseif ($node->hasAttribute('datetime')) { $value['value'] = $node->getAttribute('datetime'); $datetime = true; } elseif ($datatype === '') { $value['value'] = $node->textContent; } elseif ($datatype === self::RDF_XML_LITERAL) { $value['value'] = ''; foreach ($node->childNodes as $child) { $value['value'] .= $child->C14N(); } } elseif (is_null($datatype) and empty($rels) and empty($revs)) { $value['value'] = $this->getUriAttribute( $node, $context, array('resource', 'href', 'src') ); if ($value['value']) { $value['type'] = 'uri'; } } if (empty($value['value']) and $typedResource and !$node->hasAttribute('about')) { $value['type'] = 'uri'; $value['value'] = $typedResource; } if (empty($value['value'])) { $value['value'] = $node->textContent; } if (empty($value['type'])) { $value['type'] = 'literal'; if ($datatype) { $value['datatype'] = $datatype; } elseif (isset($datetime) or $node->nodeName === 'time') { $value['datatype'] = $this->guessTimeDatatype($value['value']); } if (empty($value['datatype']) and $lang) { $value['lang'] = $lang; } } // Add each of the properties foreach ($this->processUriList($node, $context, $property) as $prop) { if ($node->hasAttribute('inlist')) { $this->addToList($listMapping, $prop, $value); } elseif ($subject) { $this->addTriple($subject, $prop, $value); } } } // Step 12: Complete the incomplete triples from the evaluation context if (!$skip and $subject and ($context['incompleteRels'] or $context['incompleteRevs'])) { foreach ($context['incompleteRels'] as $prop) { $this->addTriple( $context['subject'], $prop, array('type' => 'uri', 'value' => $subject) ); } foreach ($context['incompleteRevs'] as $prop) { $this->addTriple( $subject, $prop, array('type' => 'uri', 'value' => $context['subject']) ); } } } // Step 13: create a new evaluation context and proceed recursively if ($node->hasChildNodes()) { if ($skip) { $newContext = $context; } else { // Prepare a new evaluation context $newContext = $context; if ($object) { $newContext['object'] = $object; } elseif ($subject) { $newContext['object'] = $subject; } else { $newContext['object'] = $context['subject']; } if ($subject) { $newContext['subject'] = $subject; } $newContext['incompleteRels'] = $incompleteRels; $newContext['incompleteRevs'] = $incompleteRevs; if (isset($listMapping)) { $newContext['listMapping'] = $listMapping; } } // The language is always updated, even if skip is set $newContext['lang'] = $lang; foreach ($node->childNodes as $child) { if ($child->nodeType === XML_ELEMENT_NODE) { $this->processNode($child, $newContext, $depth+1); } } } // Step 14: create triples for lists if (!empty($listMapping)) { foreach ($listMapping as $prop => $list) { if ($context['listMapping'] !== $listMapping) { if ($this->debug) { print "Need to create triples for $prop => ".count($list)." items\n"; } $this->generateList($subject, $prop, $list); } } } } /** * Parse RDFa 1.1 into an EasyRdf\Graph * * @param Graph $graph the graph to load the data into * @param string $data the RDF document data * @param string $format the format of the input data * @param string $baseUri the base URI of the data being parsed * * @throws \EasyRdf\Exception * @return integer The number of triples added to the graph */ public function parse($graph, $data, $format, $baseUri) { parent::checkParseParams($graph, $data, $format, $baseUri); if ($format != 'rdfa') { throw new \EasyRdf\Exception( "EasyRdf\\Parser\\Rdfa does not support: {$format}" ); } // Initialise evaluation context. $context = $this->initialContext(); libxml_use_internal_errors(true); // Parse the document into DOM $doc = new \DOMDocument(); // Attempt to parse the document as strict XML, and fall back to HTML // if XML parsing fails. if ($doc->loadXML($data, LIBXML_NONET)) { if ($this->debug) { print "Document was parsed as XML."; } // Collect all xmlns namespaces defined throughout the document. $sxe = simplexml_import_dom($doc); $context['xmlns'] = $sxe->getDocNamespaces(true); unset($context['xmlns']['']); } else { $doc->loadHTML($data); if ($this->debug) { print "Document was parsed as HTML."; } } // Establish the base for both XHTML and HTML documents. $xpath = new \DOMXPath($doc); $xpath->registerNamespace('xh', "http://www.w3.org/1999/xhtml"); $nodeList = $xpath->query('/xh:html/xh:head/xh:base'); if ($node = $nodeList->item(0) and $href = $node->getAttribute('href')) { $this->baseUri = new ParsedUri($href); } $nodeList = $xpath->query('/html/head/base'); if ($node = $nodeList->item(0) and $href = $node->getAttribute('href')) { $this->baseUri = new ParsedUri($href); } // Remove the fragment from the base URI $this->baseUri->setFragment(null); // Recursively process XML nodes $this->processNode($doc, $context); return $this->tripleCount; } } easyrdf-1.0.0/lib/Parser/Turtle.php000066400000000000000000001140531370344162000171430ustar00rootroot00000000000000data = $data; $this->namespaces = array(); $this->subject = null; $this->predicate = null; $this->object = null; $this->line = 1; $this->column = 1; $this->bytePos = 0; $this->dataLength = null; $this->resetBnodeMap(); $c = $this->skipWSC(); while ($c != -1) { $this->parseStatement(); $c = $this->skipWSC(); } return $this->tripleCount; } /** * Parse a statement [2] * @ignore */ protected function parseStatement() { $directive = ''; while (true) { $c = $this->read(); if ($c == -1 || self::isWhitespace($c)) { $this->unread($c); break; } else { $directive .= $c; } } if (preg_match('/^(@|prefix$|base$)/i', $directive)) { $this->parseDirective($directive); $this->skipWSC(); // SPARQL BASE and PREFIX lines do not end in . if ($directive[0] == "@") { $this->verifyCharacterOrFail($this->read(), "."); } } else { $this->unread($directive); $this->parseTriples(); $this->skipWSC(); $this->verifyCharacterOrFail($this->read(), "."); } } /** * Parse a directive [3] * @ignore */ protected function parseDirective($directive) { $directive = strtolower($directive); if ($directive == "prefix" || $directive == '@prefix') { $this->parsePrefixID(); } elseif ($directive == "base" || $directive == '@base') { $this->parseBase(); } elseif (mb_strlen($directive, "UTF-8") == 0) { throw new Exception( "Turtle Parse Error: directive name is missing, expected @prefix or @base", $this->line, $this->column ); } else { throw new Exception( "Turtle Parse Error: unknown directive \"$directive\"", $this->line, $this->column ); } } /** * Parse a prefixID [4] * @ignore */ protected function parsePrefixID() { $this->skipWSC(); // Read prefix ID (e.g. "rdf:" or ":") $prefixID = ''; while (true) { $c = $this->read(); if ($c == ':') { $this->unread($c); break; } elseif (self::isWhitespace($c)) { break; } elseif ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading prefix id", $this->line, $this->column ); } $prefixID .= $c; } $this->skipWSC(); $this->verifyCharacterOrFail($this->read(), ":"); $this->skipWSC(); // Read the namespace URI $namespace = $this->parseURI(); // Store local namespace mapping $this->namespaces[$prefixID] = $namespace['value']; } /** * Parse base [5] * @ignore */ protected function parseBase() { $this->skipWSC(); $baseUri = $this->parseURI(); $this->baseUri = new ParsedUri($baseUri['value']); } /** * Parse triples [6] modified to use a pointer instead of * manipulating the input buffer directly. * @ignore */ protected function parseTriples() { $c = $this->peek(); // If the first character is an open bracket we need to decide which of // the two parsing methods for blank nodes to use if ($c == '[') { $c = $this->read(); $this->skipWSC(); $c = $this->peek(); if ($c == ']') { $c = $this->read(); $this->subject = $this->createBNode(); $this->skipWSC(); $this->parsePredicateObjectList(); } else { $this->unskipWS(); $this->unread('['); $this->subject = $this->parseImplicitBlank(); } $this->skipWSC(); $c = $this->peek(); // if this is not the end of the statement, recurse into the list of // predicate and objects, using the subject parsed above as the subject // of the statement. if ($c != '.') { $this->parsePredicateObjectList(); } } else { $this->parseSubject(); $this->skipWSC(); $this->parsePredicateObjectList(); } $this->subject = null; $this->predicate = null; $this->object = null; } /** * Parse a predicateObjectList [7] * @ignore */ protected function parsePredicateObjectList() { $this->predicate = $this->parsePredicate(); $this->skipWSC(); $this->parseObjectList(); while ($this->skipWSC() == ';') { $this->read(); $c = $this->skipWSC(); if ($c == '.' || $c == ']') { break; } elseif ($c == ';') { // empty predicateObjectList, skip to next continue; } $this->predicate = $this->parsePredicate(); $this->skipWSC(); $this->parseObjectList(); } } /** * Parse a objectList [8] * @ignore */ protected function parseObjectList() { $this->parseObject(); while ($this->skipWSC() == ',') { $this->read(); $this->skipWSC(); $this->parseObject(); } } /** * Parse a subject [10] * @ignore */ protected function parseSubject() { $c = $this->peek(); if ($c == '(') { $this->subject = $this->parseCollection(); } elseif ($c == '[') { $this->subject = $this->parseImplicitBlank(); } else { $value = $this->parseValue(); if ($value['type'] == 'uri' or $value['type'] == 'bnode') { $this->subject = $value; } else { throw new Exception( "Turtle Parse Error: illegal subject type: ".$value['type'], $this->line, $this->column ); } } } /** * Parse a predicate [11] * @ignore */ protected function parsePredicate() { // Check if the short-cut 'a' is used $c1 = $this->read(); if ($c1 == 'a') { $c2 = $this->read(); if (self::isWhitespace($c2)) { // Short-cut is used, return the rdf:type URI return array( 'type' => 'uri', 'value' => RdfNamespace::get('rdf') . 'type' ); } // Short-cut is not used, unread all characters $this->unread($c2); } $this->unread($c1); // Predicate is a normal resource $predicate = $this->parseValue(); if ($predicate['type'] == 'uri') { return $predicate; } else { throw new Exception( "Turtle Parse Error: Illegal predicate type: " . $predicate['type'], $this->line, $this->column ); } } /** * Parse a object [12] * @ignore */ protected function parseObject() { $c = $this->peek(); if ($c == '(') { $this->object = $this->parseCollection(); } elseif ($c == '[') { $this->object = $this->parseImplicitBlank(); } else { $this->object = $this->parseValue(); } $this->addTriple( $this->subject['value'], $this->predicate['value'], $this->object ); } /** * Parses a blankNodePropertyList [15] * * This method parses the token [] * and predicateObjectLists that are surrounded by square brackets. * * @ignore */ protected function parseImplicitBlank() { $this->verifyCharacterOrFail($this->read(), "["); $bnode = $this->createBNode(); $c = $this->read(); if ($c != ']') { $this->unread($c); // Remember current subject and predicate $oldSubject = $this->subject; $oldPredicate = $this->predicate; // generated bNode becomes subject $this->subject = $bnode; // Enter recursion with nested predicate-object list $this->skipWSC(); $this->parsePredicateObjectList(); $this->skipWSC(); // Read closing bracket $this->verifyCharacterOrFail($this->read(), "]"); // Restore previous subject and predicate $this->subject = $oldSubject; $this->predicate = $oldPredicate; } return $bnode; } /** * Parses a collection [16], e.g: ( item1 item2 item3 ) * @ignore */ protected function parseCollection() { $this->verifyCharacterOrFail($this->read(), "("); $c = $this->skipWSC(); if ($c == ')') { // Empty list $this->read(); return array( 'type' => 'uri', 'value' => RdfNamespace::get('rdf') . 'nil' ); } else { $listRoot = $this->createBNode(); // Remember current subject and predicate $oldSubject = $this->subject; $oldPredicate = $this->predicate; // generated bNode becomes subject, predicate becomes rdf:first $this->subject = $listRoot; $this->predicate = array( 'type' => 'uri', 'value' => RdfNamespace::get('rdf') . 'first' ); $this->parseObject(); $bNode = $listRoot; while ($this->skipWSC() != ')') { // Create another list node and link it to the previous $newNode = $this->createBNode(); $this->addTriple( $bNode['value'], RdfNamespace::get('rdf') . 'rest', $newNode ); // New node becomes the current $this->subject = $bNode = $newNode; $this->parseObject(); } // Skip ')' $this->read(); // Close the list $this->addTriple( $bNode['value'], RdfNamespace::get('rdf') . 'rest', array( 'type' => 'uri', 'value' => RdfNamespace::get('rdf') . 'nil' ) ); // Restore previous subject and predicate $this->subject = $oldSubject; $this->predicate = $oldPredicate; return $listRoot; } } /** * Parses an RDF value. This method parses uriref, qname, node ID, quoted * literal, integer, double and boolean. * @ignore */ protected function parseValue() { $c = $this->peek(); if ($c == '<') { // uriref, e.g. return $this->parseURI(); } elseif ($c == ':' || self::isPrefixStartChar($c)) { // qname or boolean return $this->parseQNameOrBoolean(); } elseif ($c == '_') { // node ID, e.g. _:n1 return $this->parseNodeID(); } elseif ($c == '"' || $c == "'") { // quoted literal, e.g. "foo" or """foo""" or 'foo' or '''foo''' return $this->parseQuotedLiteral(); } elseif (ctype_digit($c) || $c == '.' || $c == '+' || $c == '-') { // integer or double, e.g. 123 or 1.2e3 return $this->parseNumber(); } elseif ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading value", $this->line, $this->column ); } else { throw new Exception( "Turtle Parse Error: expected an RDF value here, found '$c'", $this->line, $this->column ); } } /** * Parses a quoted string, optionally followed by a language tag or datatype. * @ignore */ protected function parseQuotedLiteral() { $label = $this->parseQuotedString(); // Check for presence of a language tag or datatype $c = $this->peek(); if ($c == '@') { $this->read(); // Read language $lang = ''; $c = $this->read(); if ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading language", $this->line, $this->column ); } elseif (!self::isLanguageStartChar($c)) { throw new Exception( "Turtle Parse Error: expected a letter, found '$c'", $this->line, $this->column ); } $lang .= $c; $c = $this->read(); while (!self::isWhitespace($c)) { if ($c == '.' || $c == ';' || $c == ',' || $c == ')' || $c == ']' || $c == -1) { break; } if (self::isLanguageChar($c)) { $lang .= $c; } else { throw new Exception( "Turtle Parse Error: illegal language tag char: '$c'", $this->line, $this->column ); } $c = $this->read(); } $this->unread($c); return array( 'type' => 'literal', 'value' => $label, 'lang' => $lang ); } elseif ($c == '^') { $this->read(); // next character should be another '^' $this->verifyCharacterOrFail($this->read(), "^"); // Read datatype $datatype = $this->parseValue(); if ($datatype['type'] == 'uri') { return array( 'type' => 'literal', 'value' => $label, 'datatype' => $datatype['value'] ); } else { throw new Exception( "Turtle Parse Error: illegal datatype type: " . $datatype['type'], $this->line, $this->column ); } } else { return array( 'type' => 'literal', 'value' => $label ); } } /** * Parses a quoted string, which is either a "normal string" or a """long string""". * @ignore */ protected function parseQuotedString() { $result = null; $c1 = $this->read(); // First character should be ' or " $this->verifyCharacterOrFail($c1, "\"\'"); // Check for long-string, which starts and ends with three double quotes $c2 = $this->read(); $c3 = $this->read(); if ($c2 == $c1 && $c3 == $c1) { // Long string $result = $this->parseLongString($c2); } else { // Normal string $this->unread($c3); $this->unread($c2); $result = $this->parseString($c1); } // Unescape any escape sequences return $this->unescapeString($result); } /** * Parses a "normal string". This method requires that the opening character * has already been parsed. * * @param string $closingCharacter The type of quote to use (either ' or ") * * @throws Exception * @return string * @ignore */ protected function parseString($closingCharacter) { $str = ''; while (true) { $c = $this->read(); if ($c == $closingCharacter) { break; } elseif ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading string", $this->line, $this->column ); } $str .= $c; if ($c == '\\') { // This escapes the next character, which might be a ' or a " $c = $this->read(); if ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading string", $this->line, $this->column ); } $str .= $c; } } return $str; } /** * Parses a """long string""". This method requires that the first three * characters have already been parsed. * * @param string $closingCharacter The type of quote to use (either ' or ") * * @throws Exception * @return string * @ignore */ protected function parseLongString($closingCharacter) { $str = ''; $doubleQuoteCount = 0; while ($doubleQuoteCount < 3) { $c = $this->read(); if ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading long string", $this->line, $this->column ); } elseif ($c == $closingCharacter) { $doubleQuoteCount++; } else { $doubleQuoteCount = 0; } $str .= $c; if ($c == '\\') { // This escapes the next character, which might be a ' or " $c = $this->read(); if ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading long string", $this->line, $this->column ); } $str .= $c; } } return mb_substr($str, 0, -3, "UTF-8"); } /** * Parses a numeric value, either of type integer, decimal or double * @ignore */ protected function parseNumber() { $value = ''; $datatype = RdfNamespace::get('xsd').'integer'; $c = $this->read(); // read optional sign character if ($c == '+' || $c == '-') { $value .= $c; $c = $this->read(); } while (ctype_digit($c)) { $value .= $c; $c = $this->read(); } if ($c == '.' || $c == 'e' || $c == 'E') { // read optional fractional digits if ($c == '.') { if (self::isWhitespace($this->peek())) { // We're parsing an integer that did not have a space before the // period to end the statement } else { $value .= $c; $c = $this->read(); while (ctype_digit($c)) { $value .= $c; $c = $this->read(); } if (mb_strlen($value, "UTF-8") == 1) { // We've only parsed a '.' throw new Exception( "Turtle Parse Error: object for statement missing", $this->line, $this->column ); } // We're parsing a decimal or a double $datatype = RdfNamespace::get('xsd').'decimal'; } } else { if (mb_strlen($value, "UTF-8") == 0) { // We've only parsed an 'e' or 'E' throw new Exception( "Turtle Parse Error: object for statement missing", $this->line, $this->column ); } } // read optional exponent if ($c == 'e' || $c == 'E') { $datatype = RdfNamespace::get('xsd').'double'; $value .= $c; $c = $this->read(); if ($c == '+' || $c == '-') { $value .= $c; $c = $this->read(); } if (!ctype_digit($c)) { throw new Exception( "Turtle Parse Error: exponent value missing", $this->line, $this->column ); } $value .= $c; $c = $this->read(); while (ctype_digit($c)) { $value .= $c; $c = $this->read(); } } } // Unread last character, it isn't part of the number $this->unread($c); // Return result as a typed literal return array( 'type' => 'literal', 'value' => $value, 'datatype' => $datatype ); } /** * Parses a URI / IRI * @ignore */ protected function parseURI() { $uri = ''; // First character should be '<' $this->verifyCharacterOrFail($this->read(), "<"); // Read up to the next '>' character while (true) { $c = $this->read(); if ($c == '>') { break; } elseif ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading URI", $this->line, $this->column ); } $uri .= $c; if ($c == '\\') { // This escapes the next character, which might be a '>' $c = $this->read(); if ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading URI", $this->line, $this->column ); } $uri .= $c; } } // Unescape any escape sequences $uri = $this->unescapeString($uri); return array( 'type' => 'uri', 'value' => $this->resolve($uri) ); } /** * Parses qnames and boolean values, which have equivalent starting * characters. * @ignore */ protected function parseQNameOrBoolean() { // First character should be a ':' or a letter $c = $this->read(); if ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while readying value", $this->line, $this->column ); } if ($c != ':' && !self::isPrefixStartChar($c)) { throw new Exception( "Turtle Parse Error: expected a ':' or a letter, found '$c'", $this->line, $this->column ); } $namespace = null; if ($c == ':') { // qname using default namespace if (isset($this->namespaces[''])) { $namespace = $this->namespaces['']; } else { throw new Exception( "Turtle Parse Error: default namespace used but not defined", $this->line, $this->column ); } } else { // $c is the first letter of the prefix $prefix = $c; $c = $this->read(); while (self::isPrefixChar($c)) { $prefix .= $c; $c = $this->read(); } if ($c != ':') { // prefix may actually be a boolean value $value = $prefix; if ($value == "true" || $value == "false") { return array( 'type' => 'literal', 'value' => $value, 'datatype' => RdfNamespace::get('xsd') . 'boolean' ); } } $this->verifyCharacterOrFail($c, ":"); if (isset($this->namespaces[$prefix])) { $namespace = $this->namespaces[$prefix]; } else { throw new Exception( "Turtle Parse Error: namespace prefix '$prefix' used but not defined", $this->line, $this->column ); } } // $c == ':', read optional local name $localName = ''; $c = $this->read(); if (self::isNameStartChar($c)) { if ($c == '\\') { $localName .= $this->readLocalEscapedChar(); } else { $localName .= $c; } $c = $this->read(); while (self::isNameChar($c)) { if ($c == '\\') { $localName .= $this->readLocalEscapedChar(); } else { $localName .= $c; } $c = $this->read(); } } // Unread last character $this->unread($c); // Note: namespace has already been resolved return array( 'type' => 'uri', 'value' => $namespace . $localName ); } protected function readLocalEscapedChar() { $c = $this->read(); if (self::isLocalEscapedChar($c)) { return $c; } else { throw new Exception( "found '" . $c . "', expected one of: " . implode(', ', self::$localEscapedChars), $this->line, $this->column ); } } /** * Parses a blank node ID, e.g: _:node1 * @ignore */ protected function parseNodeID() { // Node ID should start with "_:" $this->verifyCharacterOrFail($this->read(), "_"); $this->verifyCharacterOrFail($this->read(), ":"); // Read the node ID $c = $this->read(); if ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file while reading node id", $this->line, $this->column ); } elseif (!self::isNameStartChar($c)) { throw new Exception( "Turtle Parse Error: expected a letter, found '$c'", $this->line, $this->column ); } // Read all following letter and numbers, they are part of the name $name = $c; $c = $this->read(); while (self::isNameChar($c)) { $name .= $c; $c = $this->read(); } $this->unread($c); return array( 'type' => 'bnode', 'value' => $this->remapBnode($name) ); } protected function resolve($uri) { if ($this->baseUri) { return $this->baseUri->resolve($uri)->toString(); } else { return $uri; } } /** * Verifies that the supplied character $c is one of the expected * characters specified in $expected. This method will throw a * exception if this is not the case. * @ignore */ protected function verifyCharacterOrFail($c, $expected) { if ($c == -1) { throw new Exception( "Turtle Parse Error: unexpected end of file", $this->line, $this->column ); } elseif (strpbrk($c, $expected) === false) { $msg = 'expected '; for ($i = 0; $i < strlen($expected); $i++) { if ($i > 0) { $msg .= " or "; } $msg .= '\''.$expected[$i].'\''; } $msg .= ", found '$c'"; throw new Exception( "Turtle Parse Error: $msg", $this->line, $this->column ); } } /** * Skip through whitespace and comments * @ignore */ protected function skipWSC() { $c = $this->read(); while (self::isWhitespace($c) || $c == '#') { if ($c == '#') { $this->processComment(); } $c = $this->read(); } $this->unread($c); return $c; } /** * Consumes characters from reader until the first EOL has been read. * @ignore */ protected function processComment() { $comment = ''; $c = $this->read(); while ($c != -1 && $c != "\r" && $c != "\n") { $comment .= $c; $c = $this->read(); } // c is equal to -1, \r or \n. // In case c is equal to \r, we should also read a following \n. if ($c == "\r") { $c = $this->read(); if ($c != "\n") { $this->unread($c); } } } /** * Read a single character from the input buffer. * Returns -1 when the end of the file is reached. * Does not manipulate the data variable. Keeps track of the * byte position instead. * @ignore */ protected function read() { $char = $this->peek(); if ($char == -1) { return -1; } $this->bytePos += strlen($char); // Keep tracks of which line we are on (0A = Line Feed) if ($char == "\x0A") { $this->line += 1; $this->column = 1; } else { $this->column += 1; } return $char; } /** * Gets the next character to be returned by read() * without moving the pointer position. Speeds up the * mb_substr() call by only giving it the next 4 bytes to parse. * @ignore */ protected function peek() { if (!$this->dataLength) { $this->dataLength = strlen($this->data); } if ($this->dataLength > $this->bytePos) { $slice = substr($this->data, $this->bytePos, 4); return mb_substr($slice, 0, 1, "UTF-8"); } else { return -1; } } /** * Steps back, restoring the previous character read() to the input buffer */ protected function unread($chars) { $this->column -= mb_strlen($chars, "UTF-8"); $this->bytePos -= strlen($chars); if ($this->bytePos < 0) { $this->bytePos = 0; } if ($this->column < 1) { $this->column = 1; } } /** * Reverse skips through whitespace in 4 byte increments. * (Keeps the byte pointer accurate when unreading.) * @ignore */ protected function unskipWS() { if ($this->bytePos - 4 > 0) { $slice = substr($this->data, $this->bytePos - 4, 4); while ($slice != '') { if (!self::isWhitespace(mb_substr($slice, -1, 1, "UTF-8"))) { return; } $slice = substr($slice, 0, -1); $this->bytePos -= 1; } // This 4 byte slice was full of whitespace. // We need to check that there isn't more in the next slice. $this->unSkipWS(); } } /** @ignore */ protected function createBNode() { return array( 'type' => 'bnode', 'value' => $this->graph->newBNodeId() ); } /** * Returns true if $c is a whitespace character * @ignore */ public static function isWhitespace($c) { // Whitespace character are space, tab, newline and carriage return: return $c == "\x20" || $c == "\x09" || $c == "\x0A" || $c == "\x0D"; } /** @ignore */ public static function isPrefixStartChar($c) { $o = ord($c); return $o >= 0x41 && $o <= 0x5a || # A-Z $o >= 0x61 && $o <= 0x7a || # a-z $o >= 0x00C0 && $o <= 0x00D6 || $o >= 0x00D8 && $o <= 0x00F6 || $o >= 0x00F8 && $o <= 0x02FF || $o >= 0x0370 && $o <= 0x037D || $o >= 0x037F && $o <= 0x1FFF || $o >= 0x200C && $o <= 0x200D || $o >= 0x2070 && $o <= 0x218F || $o >= 0x2C00 && $o <= 0x2FEF || $o >= 0x3001 && $o <= 0xD7FF || $o >= 0xF900 && $o <= 0xFDCF || $o >= 0xFDF0 && $o <= 0xFFFD || $o >= 0x10000 && $o <= 0xEFFFF; } /** @ignore */ public static function isNameStartChar($c) { return $c == '\\' || $c == '_' || $c == ':' || $c == '%' || ctype_digit($c) || self::isPrefixStartChar($c); } /** @ignore */ public static function isNameChar($c) { $o = ord($c); return self::isNameStartChar($c) || $o >= 0x30 && $o <= 0x39 || # 0-9 $c == '-' || $o == 0x00B7 || $o >= 0x0300 && $o <= 0x036F || $o >= 0x203F && $o <= 0x2040; } /** @ignore */ private static $localEscapedChars = array( '_', '~', '.', '-', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', '/', '?', '#', '@', '%' ); /** @ignore */ public static function isLocalEscapedChar($c) { return in_array($c, self::$localEscapedChars); } /** @ignore */ public static function isPrefixChar($c) { $o = ord($c); return $c == '_' || $o >= 0x30 && $o <= 0x39 || # 0-9 self::isPrefixStartChar($c) || $c == '-' || $o == 0x00B7 || $c >= 0x0300 && $c <= 0x036F || $c >= 0x203F && $c <= 0x2040; } /** @ignore */ public static function isLanguageStartChar($c) { $o = ord($c); return $o >= 0x41 && $o <= 0x5a || # A-Z $o >= 0x61 && $o <= 0x7a; # a-z } /** @ignore */ public static function isLanguageChar($c) { $o = ord($c); return $o >= 0x41 && $o <= 0x5a || # A-Z $o >= 0x61 && $o <= 0x7a || # a-z $o >= 0x30 && $o <= 0x39 || # 0-9 $c == '-'; } } easyrdf-1.0.0/lib/RdfNamespace.php000066400000000000000000000374041370344162000167640ustar00rootroot00000000000000 'https://www.w3.org/ns/activitystreams#', 'bibo' => 'http://purl.org/ontology/bibo/', 'cc' => 'http://creativecommons.org/ns#', 'cert' => 'http://www.w3.org/ns/auth/cert#', 'csvw' => 'http://www.w3.org/ns/csvw#', 'ctag' => 'http://commontag.org/ns#', 'dc' => 'http://purl.org/dc/terms/', 'dc11' => 'http://purl.org/dc/elements/1.1/', 'dcat' => 'http://www.w3.org/ns/dcat#', 'dcterms' => 'http://purl.org/dc/terms/', 'doap' => 'http://usefulinc.com/ns/doap#', 'dqv' => 'http://www.w3.org/ns/dqv#', 'duv' => 'https://www.w3.org/ns/duv#', 'exif' => 'http://www.w3.org/2003/12/exif/ns#', 'foaf' => 'http://xmlns.com/foaf/0.1/', 'geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#', 'gr' => 'http://purl.org/goodrelations/v1#', 'grddl' => 'http://www.w3.org/2003/g/data-view#', 'ical' => 'http://www.w3.org/2002/12/cal/icaltzd#', 'jsonld' => 'http://www.w3.org/ns/json-ld#', 'ldp' => 'http://www.w3.org/ns/ldp#', 'ma' => 'http://www.w3.org/ns/ma-ont#', 'oa' => 'http://www.w3.org/ns/oa#', 'odrl' => 'http://www.w3.org/ns/odrl/2/', 'og' => 'http://ogp.me/ns#', 'org' => 'http://www.w3.org/ns/org#', 'owl' => 'http://www.w3.org/2002/07/owl#', 'prov' => 'http://www.w3.org/ns/prov#', 'qb' => 'http://purl.org/linked-data/cube#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'rdfa' => 'http://www.w3.org/ns/rdfa#', 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', 'rev' => 'http://purl.org/stuff/rev#', 'rif' => 'http://www.w3.org/2007/rif#', 'rr' => 'http://www.w3.org/ns/r2rml#', 'rss' => 'http://purl.org/rss/1.0/', 'schema' => 'http://schema.org/', 'sd' => 'http://www.w3.org/ns/sparql-service-description#', 'sioc' => 'http://rdfs.org/sioc/ns#', 'skos' => 'http://www.w3.org/2004/02/skos/core#', 'skosxl' => 'http://www.w3.org/2008/05/skos-xl#', 'sosa' => 'http://www.w3.org/ns/sosa/', 'ssn' => 'http://www.w3.org/ns/ssn/', 'synd' => 'http://purl.org/rss/1.0/modules/syndication/', 'time' => 'http://www.w3.org/2006/time#', 'v' => 'http://rdf.data-vocabulary.org/#', 'vcard' => 'http://www.w3.org/2006/vcard/ns#', 'void' => 'http://rdfs.org/ns/void#', 'wdr' => 'http://www.w3.org/2007/05/powder#', 'wdrs' => 'http://www.w3.org/2007/05/powder-s#', 'wot' => 'http://xmlns.com/wot/0.1/', 'xhv' => 'http://www.w3.org/1999/xhtml/vocab#', 'xml' => 'http://www.w3.org/XML/1998/namespace', 'xsd' => 'http://www.w3.org/2001/XMLSchema#', ); private static $namespaces = null; private static $default = null; /** Counter for numbering anonymous namespaces */ private static $anonymousNamespaceCount = 0; /** * Return all the namespaces registered * * @return array Associative array of all the namespaces. */ public static function namespaces() { if (self::$namespaces === null) { self::resetNamespaces(); } return self::$namespaces; } /** * Resets list of namespaces to the one, which is provided by EasyRDF * useful for tests, among other things */ public static function resetNamespaces() { self::$namespaces = self::$initial_namespaces; } /** * Return a namespace given its prefix. * * @param string $prefix The namespace prefix (eg 'foaf') * * @throws \InvalidArgumentException * @return string The namespace URI (eg 'http://xmlns.com/foaf/0.1/') */ public static function get($prefix) { if (!is_string($prefix) or $prefix === null) { throw new \InvalidArgumentException( "\$prefix should be a string and cannot be null or empty" ); } if (preg_match('/\W/', $prefix)) { throw new \InvalidArgumentException( "\$prefix should only contain alpha-numeric characters" ); } $prefix = strtolower($prefix); $namespaces = self::namespaces(); if (array_key_exists($prefix, $namespaces)) { return $namespaces[$prefix]; } else { return null; } } /** * Register a new namespace. * * @param string $prefix The namespace prefix (eg 'foaf') * @param string $long The namespace URI (eg 'http://xmlns.com/foaf/0.1/') * * @throws \LogicException * @throws \InvalidArgumentException */ public static function set($prefix, $long) { if (!is_string($prefix) or $prefix === null) { throw new \InvalidArgumentException( "\$prefix should be a string and cannot be null or empty" ); } if ($prefix !== '') { // prefix ::= Name minus ":" // see: http://www.w3.org/TR/REC-xml-names/#NT-NCName // Name ::= NameStartChar (NameChar)* // see: http://www.w3.org/TR/REC-xml/#NT-Name // NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | // [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | // [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] // NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] $_name_start_char = 'A-Z_a-z\xc0-\xD6\xd8-\xf6\xf8-\xff\x{0100}-\x{02ff}\x{0370}-\x{037d}' . '\x{037F}-\x{1FFF}\x{200C}-\x{200D}\x{2070}-\x{218F}\x{2C00}-\x{2FEF}\x{3001}-\x{D7FF}' . '\x{F900}-\x{FDCF}\x{FDF0}-\x{FFFD}\x{10000}-\x{EFFFF}'; $_name_char = $_name_start_char . '\-.0-9\xb7\x{0300}-\x{036f}\x{203f}-\x{2040}'; $regex = "#^[{$_name_start_char}]{1}[{$_name_char}]{0,}$#u"; $match_result = preg_match($regex, $prefix); if ($match_result === false) { throw new \LogicException('regexp error'); } if ($match_result === 0) { throw new \InvalidArgumentException( "\$prefix should match RDFXML-QName specification. got: {$prefix}" ); } } if (!is_string($long) or $long === null or $long === '') { throw new \InvalidArgumentException( "\$long should be a string and cannot be null or empty" ); } $prefix = strtolower($prefix); $namespaces = self::namespaces(); $namespaces[$prefix] = $long; self::$namespaces = $namespaces; } /** * Get the default namespace * * Returns the URI of the default namespace or null * if no default namespace is defined. * * @return string The URI of the default namespace */ public static function getDefault() { return self::$default; } /** * Set the default namespace * * Set the default namespace to either a URI or the prefix of * an already defined namespace. * * Example: * EasyRdf\RdfNamespace::setDefault('http://schema.org/'); * * @param string $namespace The URI or prefix of a namespace (eg 'og') * * @throws \InvalidArgumentException */ public static function setDefault($namespace) { if (is_null($namespace) or $namespace === '') { self::$default = null; } elseif (preg_match('/^\w+$/', $namespace)) { $namespaces = self::namespaces(); if (!isset($namespaces[$namespace])) { throw new \InvalidArgumentException( "Unable to set default namespace to unknown prefix: $namespace" ); } self::$default = $namespaces[$namespace]; } else { self::$default = $namespace; } } /** * Delete an existing namespace. * * @param string $prefix The namespace prefix (eg 'foaf') * * @throws \InvalidArgumentException */ public static function delete($prefix) { if (!is_string($prefix) or $prefix === null or $prefix === '') { throw new \InvalidArgumentException( "\$prefix should be a string and cannot be null or empty" ); } $prefix = strtolower($prefix); self::namespaces(); // make sure, that self::$namespaces is initialized if (isset(self::$namespaces[$prefix])) { unset(self::$namespaces[$prefix]); } } /** * Delete the anonymous namespaces and reset the counter to 0 */ public static function reset() { while (self::$anonymousNamespaceCount > 0) { self::delete('ns'.(self::$anonymousNamespaceCount-1)); self::$anonymousNamespaceCount--; } } /** * Try and breakup a URI into a prefix and local part * * If $createNamespace is true, and the URI isn't part of an existing * namespace, then EasyRdf will attempt to create a new namespace and * return the name of the new prefix (for example 'ns0', 'term'). * * If it isn't possible to split the URI, then null will be returned. * * @param string $uri The full URI (eg 'http://xmlns.com/foaf/0.1/name') * @param bool $createNamespace If true, a new namespace will be created * * @throws \InvalidArgumentException * @return array The split URI (eg 'foaf', 'name') or null */ public static function splitUri($uri, $createNamespace = false) { if ($uri === null or $uri === '') { throw new \InvalidArgumentException( "\$uri cannot be null or empty" ); } if (is_object($uri) and ($uri instanceof Resource)) { $uri = $uri->getUri(); } elseif (!is_string($uri)) { throw new \InvalidArgumentException( '$uri should be a string or EasyRdf\Resource' ); } foreach (self::namespaces() as $prefix => $long) { if (substr($uri, 0, strlen($long)) !== $long) { continue; } $local_part = substr($uri, strlen($long)); if (strpos($local_part, '/') !== false) { // we can't have '/' in local part continue; } return array($prefix, $local_part); } if ($createNamespace) { // Try and create a new namespace # FIXME: check the valid characters for an XML element name if (preg_match('/^(.+?)([\w\-]+)$/', $uri, $matches)) { $prefix = "ns".(self::$anonymousNamespaceCount++); self::set($prefix, $matches[1]); return array($prefix, $matches[2]); } } return null; } /** * Return the prefix namespace that a URI belongs to. * * @param string $uri A full URI (eg 'http://xmlns.com/foaf/0.1/name') * * @return string The prefix namespace that it is a part of(eg 'foaf') */ public static function prefixOfUri($uri) { if ($parts = self::splitUri($uri)) { return $parts[0]; } } /** * Shorten a URI by substituting in the namespace prefix. * * If $createNamespace is true, and the URI isn't part of an existing * namespace, then EasyRdf will attempt to create a new namespace and * use that namespace to shorten the URI (for example ns0:term). * * If it isn't possible to shorten the URI, then null will be returned. * * @param string $uri The full URI (eg 'http://xmlns.com/foaf/0.1/name') * @param bool $createNamespace If true, a new namespace will be created * * @return string The shortened URI (eg 'foaf:name') or null */ public static function shorten($uri, $createNamespace = false) { if ($parts = self::splitUri($uri, $createNamespace)) { return implode(':', $parts); } } /** * Expand a shortened URI (qname) back into a full URI. * * If it isn't possible to expand the qname, for example if the namespace * isn't registered, then the original string will be returned. * * @param string $shortUri The short URI (eg 'foaf:name') * * @throws \InvalidArgumentException * @return string The full URI (eg 'http://xmlns.com/foaf/0.1/name') */ public static function expand($shortUri) { if (!is_string($shortUri) or $shortUri === '') { throw new \InvalidArgumentException( "\$shortUri should be a string and cannot be null or empty" ); } if ($shortUri === 'a') { $namespaces = self::namespaces(); return $namespaces['rdf'] . 'type'; } elseif (preg_match('/^(\w+?):([\w\-]+)$/', $shortUri, $matches)) { $long = self::get($matches[1]); if ($long) { return $long . $matches[2]; } } elseif (preg_match('/^(\w+)$/', $shortUri) and isset(self::$default)) { return self::$default . $shortUri; } return $shortUri; } } easyrdf-1.0.0/lib/Resource.php000066400000000000000000000617241370344162000162250ustar00rootroot00000000000000resource('http://www.example.com/'); * */ public function __construct($uri, $graph = null) { if (!is_string($uri) or $uri == null or $uri == '') { throw new \InvalidArgumentException( "\$uri should be a string and cannot be null or empty" ); } $this->uri = $uri; // Check that $graph is an EasyRdf\Graph object if (is_object($graph) and $graph instanceof Graph) { $this->graph = $graph; } elseif (!is_null($graph)) { throw new \InvalidArgumentException( '$graph should be an EasyRdf\Graph object' ); } } /** * Return the graph that this resource belongs to * * @return Graph */ public function getGraph() { return $this->graph; } /** Returns the URI for the resource. * * @return string URI of this resource. */ public function getUri() { return $this->uri; } /** Check to see if a resource is a blank node. * * @return bool True if this resource is a blank node. */ public function isBNode() { if (substr($this->uri, 0, 2) == '_:') { return true; } else { return false; } } /** Get the identifier for a blank node * * Returns null if the resource is not a blank node. * * @return string The identifer for the bnode */ public function getBNodeId() { if (substr($this->uri, 0, 2) == '_:') { return substr($this->uri, 2); } else { return null; } } /** Get a the prefix of the namespace that this resource is part of * * This method will return null the resource isn't part of any * registered namespace. * * @return string The namespace prefix of the resource (e.g. foaf) */ public function prefix() { return RdfNamespace::prefixOfUri($this->uri); } /** Get a shortened version of the resources URI. * * This method will return the full URI if the resource isn't part of any * registered namespace. * * @return string The shortened URI of this resource (e.g. foaf:name) */ public function shorten() { return RdfNamespace::shorten($this->uri); } /** Gets the local name of the URI of this resource * * The local name is defined as the part of the URI string * after the last occurrence of the '#', ':' or '/' character. * * @return string The local name */ public function localName() { if (preg_match("|([^#:/]+)$|", $this->uri, $matches)) { return $matches[1]; } } /** Parse the URI of the resource and return as a ParsedUri object * * @return ParsedUri */ public function parseUri() { return new ParsedUri($this->uri); } /** Generates an HTML anchor tag, linking to this resource. * * If no text is given, then the URI also uses as the link text. * * @param string $text Text for the link. * @param array $options Associative array of attributes for the anchor tag * * @throws \InvalidArgumentException * @return string The HTML link string */ public function htmlLink($text = null, $options = array()) { $options = array_merge(array('href' => $this->uri), $options); if ($text === null) { $text = $this->uri; } $html = " $value) { if (!preg_match('/^[-\w]+$/', $key)) { throw new \InvalidArgumentException( "\$options should use valid attribute names as keys" ); } $html .= " ".htmlspecialchars($key)."=\"". htmlspecialchars($value)."\""; } $html .= ">".htmlspecialchars($text).""; return $html; } /** Returns the properties of the resource as an RDF/PHP associative array * * For example: * array('type' => 'uri', 'value' => 'http://www.example.com/') * * @return array The properties of the resource */ public function toRdfPhp() { if ($this->isBNode()) { return array('type' => 'bnode', 'value' => $this->uri); } else { return array('type' => 'uri', 'value' => $this->uri); } } /** Return pretty-print view of the resource * * @param string $format Either 'html' or 'text' * @param string $color The colour of the text * * @return string */ public function dumpValue($format = 'html', $color = 'blue') { return Utils::dumpResourceValue($this, $format, $color); } /** Magic method to return URI of resource when casted to string * * @return string The URI of the resource */ public function __toString() { return $this->uri; } /** Throw can exception if the resource does not belong to a graph * @ignore */ protected function checkHasGraph() { if (!$this->graph) { throw new Exception( 'EasyRdf\Resource is not part of a graph.' ); } } /** Perform a load (download of remote URI) of the resource into the graph * * The document type is optional but should be specified if it * can't be guessed or got from the HTTP headers. * * @param string $format Optional format of the data (eg. rdfxml) * * @return integer */ public function load($format = null) { $this->checkHasGraph(); return $this->graph->load($this->uri, $format); } /** Delete a property (or optionally just a specific value) * * @param string $property The name of the property (e.g. foaf:name) * @param object $value The value to delete (null to delete all values) * * @return integer */ public function delete($property, $value = null) { $this->checkHasGraph(); return $this->graph->delete($this->uri, $property, $value); } /** Add values to for a property of the resource * * Example: * $resource->add('prefix:property', 'value'); * * @param mixed $property The property name * @param mixed $value The value for the property * * @return integer The number of values added (1 or 0) */ public function add($property, $value) { $this->checkHasGraph(); return $this->graph->add($this->uri, $property, $value); } /** Add a literal value as a property of the resource * * The value can either be a single value or an array of values. * * Example: * $resource->add('dc:title', 'Title of Page'); * * @param mixed $property The property name * @param mixed $values The value or values for the property * @param string $lang The language of the literal * * @return integer The number of values added */ public function addLiteral($property, $values, $lang = null) { $this->checkHasGraph(); return $this->graph->addLiteral($this->uri, $property, $values, $lang); } /** Add a resource as a property of the resource * * Example: * $bob->add('foaf:knows', 'http://example.com/alice'); * * @param mixed $property The property name * @param mixed $resource2 The resource to be the value of the property * * @return integer The number of values added (1 or 0) */ public function addResource($property, $resource2) { $this->checkHasGraph(); return $this->graph->addResource($this->uri, $property, $resource2); } /** Set value for a property * * The new value(s) will replace the existing values for the property. * The name of the property should be a string. * If you set a property to null or an empty array, then the property * will be deleted. * * @param string $property The name of the property (e.g. foaf:name) * @param mixed $value The value for the property. * * @return integer The number of values added (1 or 0) */ public function set($property, $value) { $this->checkHasGraph(); return $this->graph->set($this->uri, $property, $value); } /** Get a single value for a property * * If multiple values are set for a property then the value returned * may be arbitrary. * * If $property is an array, then the first item in the array that matches * a property that exists is returned. * * This method will return null if the property does not exist. * * @param string|array $property The name of the property (e.g. foaf:name) * @param string $type The type of value to filter by (e.g. literal or resource) * @param string $lang The language to filter by (e.g. en) * * @return mixed A value associated with the property */ public function get($property, $type = null, $lang = null) { $this->checkHasGraph(); return $this->graph->get($this->uri, $property, $type, $lang); } /** Get a single literal value for a property of the resource * * If multiple values are set for a property then the value returned * may be arbitrary. * * This method will return null if there is not literal value for the * property. * * @param string|array $property The name of the property (e.g. foaf:name) * @param string $lang The language to filter by (e.g. en) * * @return Literal Literal value associated with the property */ public function getLiteral($property, $lang = null) { $this->checkHasGraph(); return $this->graph->get($this->uri, $property, 'literal', $lang); } /** Get a single resource value for a property of the resource * * If multiple values are set for a property then the value returned * may be arbitrary. * * This method will return null if there is not resource for the * property. * * @param string|array $property The name of the property (e.g. foaf:name) * * @return self Resource associated with the property */ public function getResource($property) { $this->checkHasGraph(); return $this->graph->get($this->uri, $property, 'resource'); } /** Get all values for a property * * This method will return an empty array if the property does not exist. * * @param string $property The name of the property (e.g. foaf:name) * @param string $type The type of value to filter by (e.g. literal) * @param string $lang The language to filter by (e.g. en) * * @return array An array of values associated with the property */ public function all($property, $type = null, $lang = null) { $this->checkHasGraph(); return $this->graph->all($this->uri, $property, $type, $lang); } /** Get all literal values for a property of the resource * * This method will return an empty array if the resource does not * has any literal values for that property. * * @param string $property The name of the property (e.g. foaf:name) * @param string $lang The language to filter by (e.g. en) * * @return array An array of values associated with the property */ public function allLiterals($property, $lang = null) { $this->checkHasGraph(); return $this->graph->all($this->uri, $property, 'literal', $lang); } /** Get all resources for a property of the resource * * This method will return an empty array if the resource does not * has any resources for that property. * * @param string $property The name of the property (e.g. foaf:name) * * @return array An array of values associated with the property */ public function allResources($property) { $this->checkHasGraph(); return $this->graph->all($this->uri, $property, 'resource'); } /** Count the number of values for a property of a resource * * This method will return 0 if the property does not exist. * * @param string $property The name of the property (e.g. foaf:name) * @param string $type The type of value to filter by (e.g. literal) * @param string $lang The language to filter by (e.g. en) * * @return integer The number of values associated with the property */ public function countValues($property, $type = null, $lang = null) { $this->checkHasGraph(); return $this->graph->countValues($this->uri, $property, $type, $lang); } /** Concatenate all values for a property into a string. * * The default is to join the values together with a space character. * This method will return an empty string if the property does not exist. * * @param string $property The name of the property (e.g. foaf:name) * @param string $glue The string to glue the values together with. * @param string $lang The language to filter by (e.g. en) * * @return string Concatenation of all the values. */ public function join($property, $glue = ' ', $lang = null) { $this->checkHasGraph(); return $this->graph->join($this->uri, $property, $glue, $lang); } /** Get a list of the full URIs for the properties of this resource. * * This method will return an empty array if the resource has no properties. * * @return array Array of full URIs */ public function propertyUris() { $this->checkHasGraph(); return $this->graph->propertyUris($this->uri); } /** Get a list of all the shortened property names (qnames) for a resource. * * This method will return an empty array if the resource has no properties. * * @return array Array of shortened URIs */ public function properties() { $this->checkHasGraph(); return $this->graph->properties($this->uri); } /** Get a list of the full URIs for the properties that point to this resource. * * @return array Array of full property URIs */ public function reversePropertyUris() { $this->checkHasGraph(); return $this->graph->reversePropertyUris($this->uri); } /** Check to see if a property exists for this resource. * * This method will return true if the property exists. * If the value parameter is given, then it will only return true * if the value also exists for that property. * * @param string $property The name of the property (e.g. foaf:name) * @param mixed $value An optional value of the property * * @return bool True if value the property exists. */ public function hasProperty($property, $value = null) { $this->checkHasGraph(); return $this->graph->hasProperty($this->uri, $property, $value); } /** Get a list of types for a resource. * * The types will each be a shortened URI as a string. * This method will return an empty array if the resource has no types. * * @return array All types assocated with the resource (e.g. foaf:Person) */ public function types() { $this->checkHasGraph(); return $this->graph->types($this->uri); } /** Get a single type for a resource. * * The type will be a shortened URI as a string. * If the resource has multiple types then the type returned * may be arbitrary. * This method will return null if the resource has no type. * * @return string A type assocated with the resource (e.g. foaf:Person) */ public function type() { $this->checkHasGraph(); return $this->graph->type($this->uri); } /** Get a single type for a resource, as a resource. * * The type will be returned as an EasyRdf\Resource. * If the resource has multiple types then the type returned * may be arbitrary. * This method will return null if the resource has no type. * * @return Resource A type assocated with the resource. */ public function typeAsResource() { $this->checkHasGraph(); return $this->graph->typeAsResource($this->uri); } /** * Get a list of types for a resource, as EasyRdf\Resource * * @return Resource[] * @throws Exception */ public function typesAsResources() { $this->checkHasGraph(); return $this->graph->typesAsResources($this->uri); } /** Check if a resource is of the specified type * * @param string $type The type to check (e.g. foaf:Person) * * @return boolean True if resource is of specified type. */ public function isA($type) { $this->checkHasGraph(); return $this->graph->isA($this->uri, $type); } /** Add one or more rdf:type properties to the resource * * @param string $types One or more types to add (e.g. foaf:Person) * * @return integer The number of types added */ public function addType($types) { $this->checkHasGraph(); return $this->graph->addType($this->uri, $types); } /** Change the rdf:type property for the resource * * Note that the PHP class of the resource will not change. * * @param string $type The new type (e.g. foaf:Person) * * @return integer The number of types added */ public function setType($type) { $this->checkHasGraph(); return $this->graph->setType($this->uri, $type); } /** Get the primary topic of this resource. * * Returns null if no primary topic is available. * * @return Resource The primary topic of this resource. */ public function primaryTopic() { $this->checkHasGraph(); return $this->graph->primaryTopic($this->uri); } /** Get a human readable label for this resource * * This method will check a number of properties for the resource * (in the order: skos:prefLabel, rdfs:label, foaf:name, dc:title) * and return an approriate first that is available. If no label * is available then it will return null. * * @param string|null $lang * * @return string A label for the resource. */ public function label($lang = null) { $this->checkHasGraph(); return $this->graph->label($this->uri, $lang); } /** Return a human readable view of the resource and its properties * * This method is intended to be a debugging aid and will * print a resource and its properties. * * @param string $format Either 'html' or 'text' * * @return string */ public function dump($format = 'html') { $this->checkHasGraph(); return $this->graph->dumpResource($this->uri, $format); } /** Magic method to get a property of a resource * * Note that only properties in the default namespace can be accessed in this way. * * Example: * $value = $resource->title; * * @see EasyRdf\RdfNamespace::setDefault() * * @param string $name The name of the property * * @return string A single value for the named property */ public function __get($name) { return $this->graph->get($this->uri, $name); } /** Magic method to set the value for a property of a resource * * Note that only properties in the default namespace can be accessed in this way. * * Example: * $resource->title = 'Title'; * * @see EasyRdf\RdfNamespace::setDefault() * * @param string $name The name of the property * @param string $value The value for the property * * @return int */ public function __set($name, $value) { return $this->graph->set($this->uri, $name, $value); } /** Magic method to check if a property exists * * Note that only properties in the default namespace can be accessed in this way. * * Example: * if (isset($resource->title)) { blah(); } * * @see EasyRdf\RdfNamespace::setDefault() * * @param string $name The name of the property * * @return bool */ public function __isset($name) { return $this->graph->hasProperty($this->uri, $name); } /** Magic method to delete a property of the resource * * Note that only properties in the default namespace can be accessed in this way. * * Example: * unset($resource->title); * * @see EasyRdf\RdfNamespace::setDefault() * * @param string $name The name of the property * * @return int */ public function __unset($name) { return $this->graph->delete($this->uri, $name); } /** * Whether a offset exists * * The return value will be casted to boolean if non-boolean was returned. * * Example: * if(isset($resource['rdfs:label'])) { } * * @link http://php.net/manual/en/arrayaccess.offsetexists.php * * @param mixed $offset An offset to check for. * * @return boolean true on success or false on failure. */ public function offsetExists($offset) { return $this->__isset($offset); } /** * Offset to retrieve * * Example: * $label = $resource['rdfs:label']; * * @link http://php.net/manual/en/arrayaccess.offsetget.php * * @param mixed $offset The offset to retrieve. * * @return mixed Can return all value types. */ public function offsetGet($offset) { return $this->__get($offset); } /** * Offset to set * * Example: * $resource['rdfs:label'] = 'label'; * * @link http://php.net/manual/en/arrayaccess.offsetset.php * * @param mixed The offset to assign the value to. * @param mixed $value The value to set. * * @return void */ public function offsetSet($offset, $value) { $this->__set($offset, $value); } /** * Offset to unset * * Example: * unset($resource['rdfs:label']); * * @link http://php.net/manual/en/arrayaccess.offsetunset.php * * @param mixed $offset The offset to unset. * * @return void */ public function offsetUnset($offset) { $this->__unset($offset); } } easyrdf-1.0.0/lib/Serialiser.php000066400000000000000000000074201370344162000165310ustar00rootroot00000000000000prefixes[$prefix] = true; } /** * Check and cleanup parameters passed to serialise() method * @ignore */ protected function checkSerialiseParams(&$format) { if (is_null($format) or $format == '') { throw new \InvalidArgumentException( '$format cannot be null or empty' ); } elseif (is_object($format) and ($format instanceof Format)) { $format = $format->getName(); } elseif (!is_string($format)) { throw new \InvalidArgumentException( '$format should be a string or an EasyRdf\Format object' ); } } /** * Protected method to get the number of reverse properties for a resource * If a resource only has a single property, the number of values for that * property is returned instead. * @ignore */ protected function reversePropertyCount($resource) { $properties = $resource->reversePropertyUris(); $count = count($properties); if ($count == 1) { $property = $properties[0]; return $resource->countValues("^<$property>"); } else { return $count; } } /** * Serialise an EasyRdf\Graph into desired format. * * @param Graph $graph An EasyRdf\Graph object. * @param Format|string $format The name of the format to convert to. * @param array $options * * @return string The RDF in the new desired format. */ abstract public function serialise(Graph $graph, $format, array $options = array()); } easyrdf-1.0.0/lib/Serialiser/000077500000000000000000000000001370344162000160155ustar00rootroot00000000000000easyrdf-1.0.0/lib/Serialiser/Arc.php000066400000000000000000000070111370344162000172320ustar00rootroot00000000000000 'RDFXML', 'turtle' => 'Turtle', 'ntriples' => 'NTriples', 'posh' => 'POSHRDF' ); /** * Constructor */ public function __construct() { if (!class_exists('ARC2')) { throw new Exception('ARC2 dependency is not installed'); } } /** * Serialise an EasyRdf\Graph into RDF format of choice. * * @param Graph $graph An EasyRdf\Graph object. * @param string $format The name of the format to convert to. * @param array $options * * @return string The RDF in the new desired format. * @throws Exception */ public function serialise(Graph $graph, $format, array $options = array()) { parent::checkSerialiseParams($format); if (array_key_exists($format, self::$supportedTypes)) { $className = self::$supportedTypes[$format]; } else { throw new Exception( "EasyRdf\\Serialiser\\Arc does not support: {$format}" ); } /** @var \ARC2_RDFSerializer $serialiser */ $serialiser = \ARC2::getSer($className); if ($serialiser) { return $serialiser->getSerializedIndex( parent::serialise($graph, 'php') ); } else { throw new Exception( "ARC2 failed to get a $className serialiser." ); } } } Format::register('posh', 'poshRDF'); easyrdf-1.0.0/lib/Serialiser/GraphViz.php000066400000000000000000000270161370344162000202660ustar00rootroot00000000000000 'utf-8'); /** * Set the path to the GraphViz 'dot' command * * Default is to search PATH for the command 'dot'. * * @param string $cmd The path to the 'dot' command. * * @return self */ public function setDotCommand($cmd) { $this->dotCommand = $cmd; return $this; } /** * Get the path to the GraphViz 'dot' command * * The default value is simply 'dot' * * @return string The path to the 'dot' command. */ public function getDotCommand() { return $this->dotCommand; } /** * Turn on/off the option to display labels instead of URIs. * * When this option is turned on, then labels for resources will * be displayed instead of the full URI of a resource. This makes * it simpler to create friendly diagrams that non-technical people * can understand. * * This option is turned off by default. * * @param bool $useLabels A boolean value to turn labels on and off * * @return GraphViz */ public function setUseLabels($useLabels) { $this->useLabels = $useLabels; return $this; } /** * Get the state of the use labels option * * @return bool The current state of the use labels option */ public function getUseLabels() { return $this->useLabels; } /** * Turn on/off the option to only display nodes and edges with labels * * When this option is turned on, then only nodes (resources and literals) * and edges (properties) will only be displayed if they have a label. You * can use this option, to create concise, diagrams of your data, rather than * the RDF. * * This option is turned off by default. * * @param bool $onlyLabelled A boolean value to enable/display only labelled items * * @return GraphViz */ public function setOnlyLabelled($onlyLabelled) { $this->onlyLabelled = $onlyLabelled; return $this; } /** * Get the state of the only Only Labelled option * * @return bool The current state of the Only Labelled option */ public function getOnlyLabelled() { return $this->onlyLabelled; } /** * Set an attribute on the GraphViz graph * * Example: * $serialiser->setAttribute('rotate', 90); * * See the GraphViz tool documentation for information about the * available attributes. * * @param string $name The name of the attribute * @param string $value The value for the attribute * * @return GraphViz */ public function setAttribute($name, $value) { $this->attributes[$name] = $value; return $this; } /** * Get an attribute of the GraphViz graph * * @param string $name Attribute name * * @return string The value of the graph attribute */ public function getAttribute($name) { return $this->attributes[$name]; } /** * Convert an EasyRdf object into a GraphViz node identifier * * @ignore */ protected function nodeName($entity) { if ($entity instanceof Resource) { if ($entity->isBNode()) { return "B".$entity->getUri(); } else { return "R".$entity->getUri(); } } else { return "L".$entity; } } /** * Internal function to escape a string into DOT safe syntax * * @ignore */ protected function escape($input) { if (preg_match('/^([a-z_][a-z_0-9]*|-?(\.[0-9]+|[0-9]+(\.[0-9]*)?))$/i', $input)) { return $input; } else { return '"'.str_replace( array("\r\n", "\n", "\r", '"'), array('\n', '\n', '\n', '\"'), $input ).'"'; } } /** * Internal function to escape an associate array of attributes and * turns it into a DOT notation string * * @ignore */ protected function escapeAttributes($array) { $items = array(); foreach ($array as $k => $v) { $items[] = $this->escape($k).'='.$this->escape($v); } return '['.implode(',', $items).']'; } /** * Internal function to create dot syntax line for either a node or an edge * * @ignore */ protected function serialiseRow($node1, $node2 = null, $attributes = array()) { $result = ' '.$this->escape($node1); if ($node2) { $result .= ' -> '.$this->escape($node2); } if (count($attributes)) { $result .= ' '.$this->escapeAttributes($attributes); } return $result.";\n"; } /** * Internal function to serialise an EasyRdf\Graph into a DOT formatted string * * @ignore */ protected function serialiseDot(Graph $graph) { $result = "digraph {\n"; // Write the graph attributes foreach ($this->attributes as $k => $v) { $result .= ' '.$this->escape($k).'='.$this->escape($v).";\n"; } // Go through each of the properties and write the edges $nodes = array(); $result .= "\n // Edges\n"; foreach ($graph->resources() as $resource) { $name1 = $this->nodeName($resource); foreach ($resource->propertyUris() as $property) { $label = null; if ($this->useLabels) { $label = $graph->resource($property)->label(); } if ($label === null) { if ($this->onlyLabelled == true) { continue; } else { $label = RdfNamespace::shorten($property); } } foreach ($resource->all("<$property>") as $value) { $name2 = $this->nodeName($value); $nodes[$name1] = $resource; $nodes[$name2] = $value; $result .= $this->serialiseRow( $name1, $name2, array('label' => $label) ); } } } ksort($nodes); $result .= "\n // Nodes\n"; foreach ($nodes as $name => $node) { $type = substr($name, 0, 1); $label = ''; if ($type == 'R') { if ($this->useLabels) { $label = $node->label(); } if (!$label) { $label = $node->shorten(); } if (!$label) { $label = $node->getURI(); } $result .= $this->serialiseRow( $name, null, array( 'URL' => $node->getURI(), 'label' => $label, 'shape' => 'ellipse', 'color' => 'blue' ) ); } elseif ($type == 'B') { if ($this->useLabels) { $label = $node->label(); } $result .= $this->serialiseRow( $name, null, array( 'label' => $label, 'shape' => 'circle', 'color' => 'green' ) ); } else { $result .= $this->serialiseRow( $name, null, array( 'label' => strval($node), 'shape' => 'record', ) ); } } $result .= "}\n"; return $result; } /** * Internal function to render a graph into an image * * @ignore */ public function renderImage(Graph $graph, $format = 'png') { $dot = $this->serialiseDot($graph); return Utils::execCommandPipe( $this->dotCommand, array("-T$format"), $dot ); } /** * Serialise an EasyRdf\Graph into a GraphViz dot document. * * Supported output format names: dot, gif, png, svg * * @param Graph $graph An EasyRdf\Graph object. * @param string $format The name of the format to convert to. * @param array $options * * @return string The RDF in the new desired format. * @throws Exception */ public function serialise(Graph $graph, $format, array $options = array()) { parent::checkSerialiseParams($format); switch ($format) { case 'dot': return $this->serialiseDot($graph); case 'png': case 'gif': case 'svg': return $this->renderImage($graph, $format); default: throw new Exception( "EasyRdf\\Serialiser\\GraphViz does not support: {$format}" ); } } } easyrdf-1.0.0/lib/Serialiser/Json.php000066400000000000000000000054251370344162000174450ustar00rootroot00000000000000toRdfPhp() as $resource => $properties) { if (array_key_exists($resource, $nodes)) { $node = $nodes[$resource]; } else { $node = $ld_graph->createNode($resource); $nodes[$resource] = $node; } foreach ($properties as $property => $values) { foreach ($values as $value) { if ($value['type'] == 'bnode' or $value['type'] == 'uri') { if (array_key_exists($value['value'], $nodes)) { $_value = $nodes[$value['value']]; } else { $_value = $ld_graph->createNode($value['value']); $nodes[$value['value']] = $_value; } } elseif ($value['type'] == 'literal') { if (isset($value['lang'])) { $_value = new LD\LanguageTaggedString($value['value'], $value['lang']); } elseif (isset($value['datatype'])) { $_value = new LD\TypedValue($value['value'], $value['datatype']); } else { $_value = $value['value']; } } else { throw new Exception( "Unable to serialise object to JSON-LD: ".$value['type'] ); } if ($property == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") { $node->addType($_value); } else { $node->addPropertyValue($property, $_value); } } } } // OPTIONS $use_native_types = !(isset($options['expand_native_types']) and $options['expand_native_types'] == true); $should_compact = (isset($options['compact']) and $options['compact'] == true); $should_frame = isset($options['frame']); // expanded form $data = $ld_graph->toJsonLd($use_native_types); if ($should_frame) { $data = LD\JsonLD::frame($data, $options['frame'], $options); } if ($should_compact) { // compact form $compact_context = isset($options['context']) ? $options['context'] : null; $compact_options = array( 'useNativeTypes' => $use_native_types, 'base' => $graph->getUri() ); $data = LD\JsonLD::compact($data, $compact_context, $compact_options); } return LD\JsonLD::toString($data); } } easyrdf-1.0.0/lib/Serialiser/Ntriples.php000066400000000000000000000175231370344162000203360ustar00rootroot00000000000000escChars[$c])) { $this->escChars[$c] = $this->escapedChar($c); } $result .= $this->escChars[$c]; } return $result; } /** * @ignore */ protected function unicodeCharNo($cUtf) { $bl = strlen($cUtf); /* binary length */ $r = 0; switch ($bl) { case 1: /* 0####### (0-127) */ $r = ord($cUtf); break; case 2: /* 110##### 10###### = 192+x 128+x */ $r = ((ord($cUtf[0]) - 192) * 64) + (ord($cUtf[1]) - 128); break; case 3: /* 1110#### 10###### 10###### = 224+x 128+x 128+x */ $r = ((ord($cUtf[0]) - 224) * 4096) + ((ord($cUtf[1]) - 128) * 64) + (ord($cUtf[2]) - 128); break; case 4: /* 1111#### 10###### 10###### 10###### = 240+x 128+x 128+x 128+x */ $r = ((ord($cUtf[0]) - 240) * 262144) + ((ord($cUtf[1]) - 128) * 4096) + ((ord($cUtf[2]) - 128) * 64) + (ord($cUtf[3]) - 128); break; } return $r; } /** * @ignore */ protected function escapedChar($c) { $no = $this->unicodeCharNo($c); /* see http://www.w3.org/TR/rdf-testcases/#ntrip_strings */ if ($no < 9) { return "\\u" . sprintf('%04X', $no); /* #x0-#x8 (0-8) */ } elseif ($no == 9) { return '\t'; /* #x9 (9) */ } elseif ($no == 10) { return '\n'; /* #xA (10) */ } elseif ($no < 13) { return "\\u" . sprintf('%04X', $no); /* #xB-#xC (11-12) */ } elseif ($no == 13) { return '\r'; /* #xD (13) */ } elseif ($no < 32) { return "\\u" . sprintf('%04X', $no); /* #xE-#x1F (14-31) */ } elseif ($no < 34) { return $c; /* #x20-#x21 (32-33) */ } elseif ($no == 34) { return '\"'; /* #x22 (34) */ } elseif ($no < 92) { return $c; /* #x23-#x5B (35-91) */ } elseif ($no == 92) { return '\\\\'; // double backslash /* #x5C (92) */ } elseif ($no < 127) { return $c; /* #x5D-#x7E (93-126) */ } elseif ($no < 65536) { return "\\u" . sprintf('%04X', $no); /* #x7F-#xFFFF (128-65535) */ } elseif ($no < 1114112) { return "\\U" . sprintf('%08X', $no); /* #x10000-#x10FFFF (65536-1114111) */ } else { return ''; /* not defined => ignore */ } } /** * @ignore */ protected function serialiseResource($res) { $escaped = $this->escapeString($res); if (substr($res, 0, 2) == '_:') { return $escaped; } else { return "<$escaped>"; } } /** * Serialise an RDF value into N-Triples * * The value can either be an array in RDF/PHP form, or * an EasyRdf\Literal or EasyRdf\Resource object. * * @param array|object $value An associative array or an object * * @throws Exception * * @return string The RDF value serialised to N-Triples */ public function serialiseValue($value) { if (is_object($value)) { $value = $value->toRdfPhp(); } if ($value['type'] == 'uri' or $value['type'] == 'bnode') { return $this->serialiseResource($value['value']); } elseif ($value['type'] == 'literal') { $escaped = $this->escapeString($value['value']); if (isset($value['lang'])) { $lang = $this->escapeString($value['lang']); return '"' . $escaped . '"' . '@' . $lang; } elseif (isset($value['datatype'])) { $datatype = $this->escapeString($value['datatype']); return '"' . $escaped . '"' . "^^<$datatype>"; } else { return '"' . $escaped . '"'; } } else { throw new Exception( "Unable to serialise object of type '".$value['type']."' to ntriples: " ); } } /** * Serialise an EasyRdf\Graph into N-Triples * * @param Graph $graph An EasyRdf\Graph object. * @param string $format The name of the format to convert to. * @param array $options * * @return string The RDF in the new desired format. * @throws Exception */ public function serialise(Graph $graph, $format, array $options = array()) { parent::checkSerialiseParams($format); if ($format == 'ntriples') { $nt = ''; foreach ($graph->toRdfPhp() as $resource => $properties) { foreach ($properties as $property => $values) { foreach ($values as $value) { $nt .= $this->serialiseResource($resource)." "; $nt .= "<" . $this->escapeString($property) . "> "; $nt .= $this->serialiseValue($value)." .\n"; } } } return $nt; } else { throw new Exception( __CLASS__." does not support: $format" ); } } } easyrdf-1.0.0/lib/Serialiser/Rapper.php000066400000000000000000000071671370344162000177720ustar00rootroot00000000000000/dev/null", $output, $status); if ($status != 0) { throw new Exception( "Failed to execute the command '$rapperCmd': " . join("\n", $output) ); } else { $this->rapperCmd = $rapperCmd; } } /** * Serialise an EasyRdf\Graph to the RDF format of choice. * * @param \EasyRdf\Graph $graph An EasyRdf\Graph object. * @param string $format The name of the format to convert to. * @param array $options * * @return string The RDF in the new desired format. * @throws Exception */ public function serialise(Graph $graph, $format, array $options = array()) { parent::checkSerialiseParams($format); $ntriples = parent::serialise($graph, 'ntriples'); // Hack to produce more concise RDF/XML if ($format == 'rdfxml') { $format = 'rdfxml-abbrev'; } return Utils::execCommandPipe( $this->rapperCmd, array( '--quiet', '--input', 'ntriples', '--output', $format, '-', 'unknown://' ), $ntriples ); } } easyrdf-1.0.0/lib/Serialiser/RdfPhp.php000066400000000000000000000055541370344162000177220ustar00rootroot00000000000000toRdfPhp(); } } easyrdf-1.0.0/lib/Serialiser/RdfXml.php000066400000000000000000000214541370344162000177300ustar00rootroot00000000000000propertyUris()); $rpcount = $this->reversePropertyCount($obj); $alreadyOutput = isset($this->outputtedResources[$obj->getUri()]); $tag = "{$indent}<{$property}"; if ($obj->isBNode()) { if ($alreadyOutput or $rpcount > 1 or $pcount == 0) { $tag .= " rdf:nodeID=\"".htmlspecialchars($obj->getBNodeId()).'"'; } } else { if ($alreadyOutput or $rpcount != 1 or $pcount == 0) { $tag .= " rdf:resource=\"".htmlspecialchars($obj->getURI()).'"'; } } if ($alreadyOutput == false and $rpcount == 1 and $pcount > 0) { $xml = $this->rdfxmlResource($obj, false, $depth+1); if ($xml) { return "$tag>$xml$indent\n\n"; } else { return ''; } } else { return $tag."/>\n"; } } elseif (is_object($obj) and $obj instanceof Literal) { $atrributes = ""; $datatype = $obj->getDatatypeUri(); if ($datatype) { if ($datatype == self::RDF_XML_LITERAL) { $atrributes .= " rdf:parseType=\"Literal\""; $value = strval($obj); } else { $datatype = htmlspecialchars($datatype); $atrributes .= " rdf:datatype=\"$datatype\""; } } elseif ($obj->getLang()) { $atrributes .= ' xml:lang="'. htmlspecialchars($obj->getLang()).'"'; } // Escape the value if (!isset($value)) { $value = htmlspecialchars(strval($obj)); } return "{$indent}<{$property}{$atrributes}>{$value}\n"; } else { throw new Exception( "Unable to serialise object to xml: ".getType($obj) ); } } /** * Protected method to serialise a whole resource and its properties * @ignore */ protected function rdfxmlResource($res, $showNodeId, $depth = 1) { // Keep track of the resources we have already serialised if (isset($this->outputtedResources[$res->getUri()])) { return ''; } else { $this->outputtedResources[$res->getUri()] = true; } // If the resource has no properties - don't serialise it $properties = $res->propertyUris(); if (count($properties) == 0) { return ''; } $type = $res->type(); if ($type) { $this->addPrefix($type); } else { $type = 'rdf:Description'; } $indent = str_repeat(' ', $depth); $xml = "\n$indent<$type"; if ($res->isBNode()) { if ($showNodeId) { $xml .= ' rdf:nodeID="'.htmlspecialchars($res->getBNodeId()).'"'; } } else { $xml .= ' rdf:about="'.htmlspecialchars($res->getUri()).'"'; } $xml .= ">\n"; if ($res instanceof Container) { foreach ($res as $item) { $xml .= $this->rdfxmlObject('rdf:li', $item, $depth+1); } } else { foreach ($properties as $property) { $short = RdfNamespace::shorten($property, true); if ($short) { $this->addPrefix($short); $objects = $res->all("<$property>"); if ($short == 'rdf:type' && $type != 'rdf:Description') { array_shift($objects); } foreach ($objects as $object) { $xml .= $this->rdfxmlObject($short, $object, $depth+1); } } else { throw new Exception( "It is not possible to serialse the property ". "'$property' to RDF/XML." ); } } } $xml .= "$indent\n"; return $xml; } /** * Method to serialise an EasyRdf\Graph to RDF/XML * * @param Graph $graph An EasyRdf\Graph object. * @param string $format The name of the format to convert to. * @param array $options * * @return string The RDF in the new desired format. * @throws Exception */ public function serialise(Graph $graph, $format, array $options = array()) { parent::checkSerialiseParams($format); if ($format != 'rdfxml') { throw new Exception( "EasyRdf\\Serialiser\\RdfXml does not support: {$format}" ); } // store of namespaces to be appended to the rdf:RDF tag $this->prefixes = array('rdf' => true); // store of the resource URIs we have serialised $this->outputtedResources = array(); $xml = ''; // Serialise URIs first foreach ($graph->resources() as $resource) { if (!$resource->isBnode()) { $xml .= $this->rdfxmlResource($resource, true); } } // Serialise bnodes afterwards foreach ($graph->resources() as $resource) { if ($resource->isBnode()) { $xml .= $this->rdfxmlResource($resource, true); } } // iterate through namepsaces array prefix and output a string. $namespaceStr = ''; foreach ($this->prefixes as $prefix => $count) { $url = RdfNamespace::get($prefix); if (strlen($namespaceStr)) { $namespaceStr .= "\n "; } if (strlen($prefix) === 0) { $namespaceStr .= ' xmlns="'.htmlspecialchars($url).'"'; } else { $namespaceStr .= ' xmlns:'.$prefix.'="'.htmlspecialchars($url).'"'; } } return "\n". "\n" . $xml . "\n\n"; } } easyrdf-1.0.0/lib/Serialiser/Turtle.php000066400000000000000000000302001370344162000200000ustar00rootroot00000000000000', '\\>', $resourceIri); return "<$escapedIri>"; } /** * Given a string, enclose in quotes and escape any quotes in the string. * Strings containing tabs, linefeeds or carriage returns will be * enclosed in three double quotes ("""). * * @param string $value * * @return string */ public static function quotedString($value) { if (preg_match('/[\t\n\r]/', $value)) { $escaped = str_replace(array('\\', '"""'), array('\\\\', '\\"""'), $value); // Check if the last character is a trailing double quote, if so, escape it. $pos = strrpos($escaped, '"'); if ($pos !== false && $pos + 1 == strlen($escaped)) { $escaped = substr($escaped, 0, -1); $escaped .= '\"'; } return '"""'.$escaped.'"""'; } else { $escaped = str_replace(array('\\', '"'), array('\\\\', '\\"'), $value); return '"'.$escaped.'"'; } } /** * Given a an EasyRdf\Resource or URI, convert it into a string, suitable to * be written to a Turtle document. URIs will be shortened into CURIES * where possible. * * @param Resource|string $resource The resource to convert to a Turtle string * @param boolean $createNamespace If true, a new namespace may be created * * @return string */ public function serialiseResource($resource, $createNamespace = false) { if (is_object($resource)) { if ($resource->isBNode()) { return $resource->getUri(); } $resource = $resource->getUri(); } $short = RdfNamespace::shorten($resource, $createNamespace); if ($short) { $this->addPrefix($short); return $short; } return self::escapeIri($resource); } /** * Given an EasyRdf\Literal object, convert it into a string, suitable to * be written to a Turtle document. Supports multiline literals and literals with * datatypes or languages. * * @param Literal $literal * * @return string */ public function serialiseLiteral($literal) { $value = strval($literal); $quoted = self::quotedString($value); if ($datatype = $literal->getDatatypeUri()) { if ($datatype == 'http://www.w3.org/2001/XMLSchema#integer') { return sprintf('%d', $value); } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#decimal') { return sprintf('%s', $value); } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#double') { return sprintf('%e', $value); } elseif ($datatype == 'http://www.w3.org/2001/XMLSchema#boolean') { return sprintf('%s', $value); } else { $escaped = $this->serialiseResource($datatype, true); return sprintf('%s^^%s', $quoted, $escaped); } } elseif ($lang = $literal->getLang()) { return $quoted . '@' . $lang; } else { return $quoted; } } /** * Convert an EasyRdf object into a string suitable to * be written to a Turtle document. * * @param Resource|Literal $object * * @throws \InvalidArgumentException * @return string */ public function serialiseObject($object) { if ($object instanceof Resource) { return $this->serialiseResource($object); } elseif ($object instanceof Literal) { return $this->serialiseLiteral($object); } else { throw new \InvalidArgumentException( "serialiseObject() requires \$object to be ". 'of type EasyRdf\Resource or EasyRdf\Literal' ); } } /** * Protected method to serialise a RDF collection * @ignore */ protected function serialiseCollection($node, $indent) { $turtle = '('; $count = 0; while ($node) { if ($id = $node->getBNodeId()) { $this->outputtedBnodes[$id] = true; } $value = $node->get('rdf:first'); $node = $node->get('rdf:rest'); if ($node and $node->hasProperty('rdf:first')) { $count++; } if ($value !== null) { $serialised = $this->serialiseObject($value); if ($count) { $turtle .= "\n$indent $serialised"; } else { $turtle .= " ".$serialised; } } } if ($count) { $turtle .= "\n$indent)"; } else { $turtle .= " )"; } return $turtle; } /** * Protected method to serialise the properties of a resource * @ignore */ protected function serialiseProperties($res, $depth = 1) { $properties = $res->propertyUris(); $indent = str_repeat(' ', ($depth*2)-1); $turtle = ''; if (count($properties) > 1) { $turtle .= "\n$indent"; } $pCount = 0; foreach ($properties as $property) { if ($property === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') { $pStr = 'a'; } else { $pStr = $this->serialiseResource($property, true); } if ($pCount) { $turtle .= " ;\n$indent"; } $turtle .= ' ' . $pStr; $oCount = 0; foreach ($res->all("<$property>") as $object) { if ($oCount) { $turtle .= ','; } if ($object instanceof Collection) { $turtle .= ' ' . $this->serialiseCollection($object, $indent); } elseif ($object instanceof Resource and $object->isBNode()) { $id = $object->getBNodeId(); $rpcount = $this->reversePropertyCount($object); if ($rpcount <= 1 and !isset($this->outputtedBnodes[$id])) { // Nested unlabelled Blank Node $this->outputtedBnodes[$id] = true; $turtle .= ' ['; $turtle .= $this->serialiseProperties($object, $depth+1); $turtle .= ' ]'; } else { // Multiple properties pointing to this blank node $turtle .= ' ' . $this->serialiseObject($object); } } else { $turtle .= ' ' . $this->serialiseObject($object); } $oCount++; } $pCount++; } if ($depth == 1) { $turtle .= " ."; if ($pCount > 1) { $turtle .= "\n"; } } elseif ($pCount > 1) { $turtle .= "\n" . str_repeat(' ', (($depth-1)*2)-1); } return $turtle; } /** * @ignore */ protected function serialisePrefixes() { $turtle = ''; foreach ($this->prefixes as $prefix => $count) { $url = RdfNamespace::get($prefix); $turtle .= "@prefix $prefix: <$url> .\n"; } return $turtle; } /** * @ignore */ protected function serialiseSubjects(Graph $graph, $filterType) { $turtle = ''; foreach ($graph->resources() as $resource) { /** @var $resource Resource */ // If the resource has no properties - don't serialise it $properties = $resource->propertyUris(); if (count($properties) == 0) { continue; } // Is this node of the right type? $thisType = $resource->isBNode() ? 'bnode' : 'uri'; if ($thisType != $filterType) { continue; } if ($thisType == 'bnode') { $id = $resource->getBNodeId(); if (isset($this->outputtedBnodes[$id])) { // Already been serialised continue; } $this->outputtedBnodes[$id] = true; $rpcount = $this->reversePropertyCount($resource); if ($rpcount == 0) { $turtle .= '[]'; } else { $turtle .= $this->serialiseResource($resource); } } else { $turtle .= $this->serialiseResource($resource); } $turtle .= $this->serialiseProperties($resource); $turtle .= "\n"; } return $turtle; } /** * Serialise an EasyRdf\Graph to Turtle. * * @param Graph $graph An EasyRdf\Graph object. * @param string $format The name of the format to convert to. * @param array $options * * @return string The RDF in the new desired format. * @throws Exception */ public function serialise(Graph $graph, $format, array $options = array()) { parent::checkSerialiseParams($format); if ($format != 'turtle' and $format != 'n3') { throw new Exception( "EasyRdf\\Serialiser\\Turtle does not support: {$format}" ); } $this->prefixes = array(); $this->outputtedBnodes = array(); $turtle = ''; $turtle .= $this->serialiseSubjects($graph, 'uri'); $turtle .= $this->serialiseSubjects($graph, 'bnode'); if (count($this->prefixes)) { return $this->serialisePrefixes() . "\n" . $turtle; } else { return $turtle; } } } easyrdf-1.0.0/lib/Sparql/000077500000000000000000000000001370344162000151555ustar00rootroot00000000000000easyrdf-1.0.0/lib/Sparql/Client.php000066400000000000000000000311251370344162000171060ustar00rootroot00000000000000queryUri = $queryUri; if (strlen(parse_url($queryUri, PHP_URL_QUERY)) > 0) { $this->queryUri_has_params = true; } else { $this->queryUri_has_params = false; } if ($updateUri) { $this->updateUri = $updateUri; } else { $this->updateUri = $queryUri; } } /** Get the URI of the SPARQL query endpoint * * @return string The query URI of the SPARQL endpoint */ public function getQueryUri() { return $this->queryUri; } /** Get the URI of the SPARQL update endpoint * * @return string The query URI of the SPARQL endpoint */ public function getUpdateUri() { return $this->updateUri; } /** * @depredated * @ignore */ public function getUri() { return $this->queryUri; } /** Make a query to the SPARQL endpoint * * SELECT and ASK queries will return an object of type * EasyRdf\Sparql\Result. * * CONSTRUCT and DESCRIBE queries will return an object * of type EasyRdf\Graph. * * @param string $query The query string to be executed * * @return Result|\EasyRdf\Graph Result of the query. */ public function query($query) { return $this->request('query', $query); } /** Count the number of triples in a SPARQL 1.1 endpoint * * Performs a SELECT query to estriblish the total number of triples. * * Counts total number of triples by default but a conditional triple pattern * can be given to count of a subset of all triples. * * @param string $condition Triple-pattern condition for the count query * * @return integer The number of triples */ public function countTriples($condition = '?s ?p ?o') { // SELECT (COUNT(*) AS ?count) // WHERE { // {?s ?p ?o} // UNION // {GRAPH ?g {?s ?p ?o}} // } $result = $this->query('SELECT (COUNT(*) AS ?count) {'.$condition.'}'); return $result[0]->count->getValue(); } /** Get a list of named graphs from a SPARQL 1.1 endpoint * * Performs a SELECT query to get a list of the named graphs * * @param string $limit Optional limit to the number of results * * @return \EasyRdf\Resource[] array of objects for each named graph */ public function listNamedGraphs($limit = null) { $query = "SELECT DISTINCT ?g WHERE {GRAPH ?g {?s ?p ?o}}"; if (!is_null($limit)) { $query .= " LIMIT ".(int)$limit; } $result = $this->query($query); // Convert the result object into an array of resources $graphs = array(); foreach ($result as $row) { array_push($graphs, $row->g); } return $graphs; } /** Make an update request to the SPARQL endpoint * * Successful responses will return the HTTP response object * * Unsuccessful responses will throw an exception * * @param string $query The update query string to be executed * * @return \EasyRdf\Http\Response HTTP response */ public function update($query) { return $this->request('update', $query); } public function insert($data, $graphUri = null) { #$this->updateData('INSET', $query = 'INSERT DATA {'; if ($graphUri) { $query .= "GRAPH <$graphUri> {"; } $query .= $this->convertToTriples($data); if ($graphUri) { $query .= "}"; } $query .= '}'; return $this->update($query); } protected function updateData($operation, $data, $graphUri = null) { $query = "$operation DATA {"; if ($graphUri) { $query .= "GRAPH <$graphUri> {"; } $query .= $this->convertToTriples($data); if ($graphUri) { $query .= "}"; } $query .= '}'; return $this->update($query); } public function clear($graphUri, $silent = false) { $query = "CLEAR"; if ($silent) { $query .= " SILENT"; } if (preg_match('/^all|named|default$/i', $graphUri)) { $query .= " $graphUri"; } else { $query .= " GRAPH <$graphUri>"; } return $this->update($query); } /* * Internal function to make an HTTP request to SPARQL endpoint * * @ignore */ protected function request($type, $query) { $processed_query = $this->preprocessQuery($query); $response = $this->executeQuery($processed_query, $type); if (!$response->isSuccessful()) { throw new Http\Exception("HTTP request for SPARQL query failed", 0, null, $response->getBody()); } if ($response->getStatus() == 204) { // No content return $response; } return $this->parseResponseToQuery($response); } protected function convertToTriples($data) { if (is_string($data)) { return $data; } elseif (is_object($data) and $data instanceof Graph) { # FIXME: insert Turtle when there is a way of seperateing out the prefixes return $data->serialise('ntriples'); } else { throw new Exception( "Don't know how to convert to triples for SPARQL query" ); } } /** * Adds missing prefix-definitions to the query * * Overriding classes may execute arbitrary query-alteration here * * @param string $query * @return string */ protected function preprocessQuery($query) { // Check for undefined prefixes $prefixes = ''; foreach (RdfNamespace::namespaces() as $prefix => $uri) { if (strpos($query, "{$prefix}:") !== false and strpos($query, "PREFIX {$prefix}:") === false ) { $prefixes .= "PREFIX {$prefix}: <{$uri}>\n"; } } return $prefixes . $query; } /** * Build http-client object, execute request and return a response * * @param string $processed_query * @param string $type Should be either "query" or "update" * * @return Http\Response|\Zend\Http\Response * @throws Exception */ protected function executeQuery($processed_query, $type) { $client = Http::getDefaultHttpClient(); $client->resetParameters(); // Tell the server which response formats we can parse $sparql_results_types = array( 'application/sparql-results+json' => 1.0, 'application/sparql-results+xml' => 0.8 ); if ($type == 'update') { // accept anything, as "response body of a […] update request is implementation defined" // @see http://www.w3.org/TR/sparql11-protocol/#update-success $accept = Format::getHttpAcceptHeader($sparql_results_types); $client->setHeaders('Accept', $accept); $client->setMethod('POST'); $client->setUri($this->updateUri); $client->setRawData($processed_query); $client->setHeaders('Content-Type', 'application/sparql-update'); } elseif ($type == 'query') { $re = '(?:(?:\s*BASE\s*<.*?>\s*)|(?:\s*PREFIX\s+.+:\s*<.*?>\s*))*'. '(CONSTRUCT|SELECT|ASK|DESCRIBE)[\W]'; $result = null; $matched = mb_eregi($re, $processed_query, $result); if (false === $matched or count($result) !== 2) { // non-standard query. is this something non-standard? $query_verb = null; } else { $query_verb = strtoupper($result[1]); } if ($query_verb === 'SELECT' or $query_verb === 'ASK') { // only "results" $accept = Format::formatAcceptHeader($sparql_results_types); } elseif ($query_verb === 'CONSTRUCT' or $query_verb === 'DESCRIBE') { // only "graph" $accept = Format::getHttpAcceptHeader(); } else { // both $accept = Format::getHttpAcceptHeader($sparql_results_types); } $client->setHeaders('Accept', $accept); $encodedQuery = 'query=' . urlencode($processed_query); // Use GET if the query is less than 2kB // 2046 = 2kB minus 1 for '?' and 1 for NULL-terminated string on server if (strlen($encodedQuery) + strlen($this->queryUri) <= 2046) { $delimiter = $this->queryUri_has_params ? '&' : '?'; $client->setMethod('GET'); $client->setUri($this->queryUri . $delimiter . $encodedQuery); } else { // Fall back to POST instead (which is un-cacheable) $client->setMethod('POST'); $client->setUri($this->queryUri); $client->setRawData($encodedQuery); $client->setHeaders('Content-Type', 'application/x-www-form-urlencoded'); } } else { throw new Exception('unexpected request-type: '.$type); } return $client->request(); } /** * Parse HTTP-response object into a meaningful result-object. * * Can be overridden to do custom processing * * @param Http\Response|\Zend\Http\Response $response * @return Graph|Result */ protected function parseResponseToQuery($response) { list($content_type,) = Utils::parseMimeType($response->getHeader('Content-Type')); if (strpos($content_type, 'application/sparql-results') === 0) { $result = new Result($response->getBody(), $content_type); return $result; } else { $result = new Graph($this->queryUri, $response->getBody(), $content_type); return $result; } } } easyrdf-1.0.0/lib/Sparql/Result.php000066400000000000000000000317661370344162000171610ustar00rootroot00000000000000parseXml($data); } elseif ($mimeType == 'application/sparql-results+json') { $this->parseJson($data); } else { throw new Exception( "Unsupported SPARQL Query Results format: $mimeType" ); } } /** Get the query result type (boolean/bindings) * * ASK queries return a result of type 'boolean'. * SELECT query return a result of type 'bindings'. * * @return string The query result type. */ public function getType() { return $this->type; } /** Return the boolean value of the query result * * If the query was of type boolean then this method will * return either true or false. If the query was of some other * type then this method will return null. * * @return boolean The result of the query. */ public function getBoolean() { return $this->boolean; } /** Return true if the result of the query was true. * * @return boolean True if the query result was true. */ public function isTrue() { return $this->boolean == true; } /** Return false if the result of the query was false. * * @return boolean True if the query result was false. */ public function isFalse() { return $this->boolean == false; } /** Return the number of fields in a query result of type bindings. * * @return integer The number of fields. */ public function numFields() { return count($this->fields); } /** Return the number of rows in a query result of type bindings. * * @return integer The number of rows. */ public function numRows() { return count($this); } /** Get the field names in a query result of type bindings. * * @return array The names of the fields in the result. */ public function getFields() { return $this->fields; } /** Return a human readable view of the query result. * * This method is intended to be a debugging aid and will * return a pretty-print view of the query result. * * @param string $format Either 'text' or 'html' * * @throws Exception * @return string */ public function dump($format = 'html') { if ($this->type == 'bindings') { $result = ''; if ($format == 'html') { $result .= ""; $result .= ""; foreach ($this->fields as $field) { $result .= ""; } $result .= ""; foreach ($this as $row) { $result .= ""; foreach ($this->fields as $field) { if (isset($row->$field)) { $result .= ""; } else { $result .= ""; } } $result .= ""; } $result .= "
". "?$field
". $row->$field->dumpValue($format)." 
"; } else { // First calculate the width of each comment $colWidths = array(); foreach ($this->fields as $field) { $colWidths[$field] = strlen($field); } $textData = array(); foreach ($this as $row) { $textRow = array(); foreach ($row as $k => $v) { $textRow[$k] = $v->dumpValue('text'); $width = strlen($textRow[$k]); if ($colWidths[$k] < $width) { $colWidths[$k] = $width; } } $textData[] = $textRow; } // Create a horizontal rule $hr = "+"; foreach ($colWidths as $v) { $hr .= "-".str_repeat('-', $v).'-+'; } // Output the field names $result .= "$hr\n|"; foreach ($this->fields as $field) { $result .= ' '.str_pad("?$field", $colWidths[$field]).' |'; } // Output each of the rows $result .= "\n$hr\n"; foreach ($textData as $textRow) { $result .= '|'; foreach ($textRow as $k => $v) { $result .= ' '.str_pad($v, $colWidths[$k]).' |'; } $result .= "\n"; } $result .= "$hr\n"; } return $result; } elseif ($this->type == 'boolean') { $str = ($this->boolean ? 'true' : 'false'); if ($format == 'html') { return "

Result: $str

"; } else { return "Result: $str"; } } else { throw new Exception( "Failed to dump SPARQL Query Results format, unknown type: ". $this->type ); } } /** Create a new EasyRdf\Resource or EasyRdf\Literal depending * on the type of data passed in. * * @ignore */ protected function newTerm($data) { switch ($data['type']) { case 'bnode': return new Resource('_:'.$data['value']); case 'uri': return new Resource($data['value']); case 'literal': case 'typed-literal': return Literal::create($data); default: throw new Exception( "Failed to parse SPARQL Query Results format, unknown term type: ". $data['type'] ); } } /** Parse a SPARQL result in the XML format into the object. * * @ignore */ protected function parseXml($data) { $doc = new \DOMDocument(); $doc->loadXML($data); # Check for valid root node. if ($doc->hasChildNodes() == false or $doc->childNodes->length != 1 or $doc->firstChild->nodeName != 'sparql' or $doc->firstChild->namespaceURI != self::SPARQL_XML_RESULTS_NS) { throw new Exception( "Incorrect root node in SPARQL XML Query Results format" ); } # Is it the result of an ASK query? $boolean = $doc->getElementsByTagName('boolean'); if ($boolean->length) { $this->type = 'boolean'; $value = $boolean->item(0)->nodeValue; $this->boolean = $value == 'true' ? true : false; return; } # Get a list of variables from the header $head = $doc->getElementsByTagName('head'); if ($head->length) { $variables = $head->item(0)->getElementsByTagName('variable'); foreach ($variables as $variable) { $this->fields[] = $variable->getAttribute('name'); } } # Is it the result of a SELECT query? $resultstag = $doc->getElementsByTagName('results'); if ($resultstag->length) { $this->type = 'bindings'; $results = $resultstag->item(0)->getElementsByTagName('result'); foreach ($results as $result) { $bindings = $result->getElementsByTagName('binding'); $t = new \stdClass(); foreach ($bindings as $binding) { $key = $binding->getAttribute('name'); foreach ($binding->childNodes as $node) { if ($node->nodeType != XML_ELEMENT_NODE) { continue; } $t->$key = $this->newTerm( array( 'type' => $node->nodeName, 'value' => $node->nodeValue, 'lang' => $node->getAttribute('xml:lang'), 'datatype' => $node->getAttribute('datatype') ) ); break; } } $this[] = $t; } return; } throw new Exception( "Failed to parse SPARQL XML Query Results format" ); } /** Parse a SPARQL result in the JSON format into the object. * * @ignore */ protected function parseJson($data) { // Decode JSON to an array $data = json_decode($data, true); if (isset($data['boolean'])) { $this->type = 'boolean'; $this->boolean = $data['boolean']; } elseif (isset($data['results'])) { $this->type = 'bindings'; if (isset($data['head']['vars'])) { $this->fields = $data['head']['vars']; } foreach ($data['results']['bindings'] as $row) { $t = new \stdClass(); foreach ($row as $key => $value) { $t->$key = $this->newTerm($value); } $this[] = $t; } } else { throw new Exception( "Failed to parse SPARQL JSON Query Results format" ); } } /** Magic method to return value of the result to string * * If this is a boolean result then it will return 'true' or 'false'. * If it is a bindings type, then it will dump as a text based table. * * @return string A string representation of the result. */ public function __toString() { if ($this->type == 'boolean') { return $this->boolean ? 'true' : 'false'; } else { return $this->dump('text'); } } } easyrdf-1.0.0/lib/TypeMapper.php000066400000000000000000000134011370344162000165110ustar00rootroot00000000000000$short"; } else { return "$escaped"; } } else { if ($short) { return $short; } else { return $resource; } } } /** Return pretty-print view of a literal * * This method is mainly intended for internal use and is used by * EasyRdf\Graph and EasyRdf\Sparql\Result to format a literal * for display. * * @param mixed $literal An EasyRdf\Literal object or an associative array * @param string $format Either 'html' or 'text' * @param string $color The colour of the text * * @throws \InvalidArgumentException * @return string */ public static function dumpLiteralValue($literal, $format = 'html', $color = 'black') { if (!preg_match('/^#?[-\w]+$/', $color)) { throw new \InvalidArgumentException( "\$color must be a legal color code or name" ); } if (is_object($literal)) { $literal = $literal->toRdfPhp(); } elseif (!is_array($literal)) { $literal = array('value' => $literal); } $text = '"'.$literal['value'].'"'; if (isset($literal['lang'])) { $text .= '@' . $literal['lang']; } if (isset($literal['datatype'])) { $short = RdfNamespace::shorten($literal['datatype']); if ($short) { $text .= "^^$short"; } else { $text .= "^^<".$literal['datatype'].">"; } } if ($format == 'html') { return "". htmlentities($text, ENT_COMPAT, "UTF-8"). ""; } else { return $text; } } /** Clean up and split a mime-type up into its parts * * @param string $mimeType A MIME Type, optionally with parameters * * @return array $type, $parameters */ public static function parseMimeType($mimeType) { $parts = explode(';', strtolower($mimeType)); $type = trim(array_shift($parts)); $params = array(); foreach ($parts as $part) { if (preg_match('/^\s*(\w+)\s*=\s*(.+?)\s*$/', $part, $matches)) { $params[$matches[1]] = $matches[2]; } } return array($type, $params); } /** Execute a command as a pipe * * The proc_open() function is used to open a pipe to a * a command line process, writing $input to STDIN, returning STDOUT * and throwing an exception if anything is written to STDERR or the * process returns non-zero. * * @param string $command The command to execute * @param array $args Optional list of arguments to pass to the command * @param string $input Optional buffer to send to the command * @param string $dir Path to directory to run command in (defaults to /tmp) * * @throws Exception * @return string The result of the command, printed to STDOUT */ public static function execCommandPipe($command, $args = null, $input = null, $dir = null) { $descriptorspec = array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w') ); // Use the system tmp directory by default if (!$dir) { $dir = sys_get_temp_dir(); } if (is_array($args)) { $fullCommand = implode( ' ', array_map('escapeshellcmd', array_merge(array($command), $args)) ); } else { $fullCommand = escapeshellcmd($command); if ($args) { $fullCommand .= ' '.escapeshellcmd($args); } } $process = proc_open($fullCommand, $descriptorspec, $pipes, $dir); if (is_resource($process)) { // $pipes now looks like this: // 0 => writeable handle connected to child stdin // 1 => readable handle connected to child stdout // 2 => readable handle connected to child stderr if ($input) { fwrite($pipes[0], $input); } fclose($pipes[0]); $output = stream_get_contents($pipes[1]); fclose($pipes[1]); $error = stream_get_contents($pipes[2]); fclose($pipes[2]); // It is important that you close any pipes before calling // proc_close in order to avoid a deadlock $returnValue = proc_close($process); if ($returnValue) { throw new Exception( "Error while executing command $command: ".$error ); } } else { throw new Exception( "Failed to execute command $command" ); } return $output; } } easyrdf-1.0.0/scripts/000077500000000000000000000000001370344162000146345ustar00rootroot00000000000000easyrdf-1.0.0/scripts/copyright_updater.php000066400000000000000000000031251370344162000211020ustar00rootroot00000000000000