Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77d695f67a | |||
| 43997a3493 | |||
| e6316504f1 | |||
| d8ac0993d6 |
@@ -4,7 +4,7 @@ services:
|
||||
|
||||
nginx:
|
||||
ports:
|
||||
- "8080:80"
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ../volume/www:/home/www:cached
|
||||
restart: "no"
|
||||
|
||||
@@ -36,7 +36,7 @@ server {
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
|
||||
location / {
|
||||
# try to serve file directly, fallback to app.php
|
||||
try_files $uri /app.php$is_args$args;
|
||||
@@ -58,6 +58,7 @@ server {
|
||||
# for more information).
|
||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||
fastcgi_param DOCUMENT_ROOT $realpath_root;
|
||||
fastcgi_param HTTPS on;
|
||||
}
|
||||
|
||||
# PROD
|
||||
@@ -78,6 +79,7 @@ server {
|
||||
# http://domain.tld/app.php/some-path
|
||||
# Remove the internal directive to allow URIs like this
|
||||
internal;
|
||||
fastcgi_param HTTPS on;
|
||||
}
|
||||
|
||||
# ---- PRESTASHOP ----
|
||||
|
||||
@@ -44,6 +44,10 @@ class AppKernel extends Kernel
|
||||
new Edeclic\PrestashopBridgeBundle\EdeclicPrestashopBridgeBundle(),
|
||||
new Edeclic\HistoryBundle\EdeclicHistoryBundle(),
|
||||
new AppBundle\AppBundle(),
|
||||
|
||||
//oAuth
|
||||
new Http\HttplugBundle\HttplugBundle(),
|
||||
new HWI\Bundle\OAuthBundle\HWIOAuthBundle(),
|
||||
];
|
||||
|
||||
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
|
||||
|
||||
+4
-4
@@ -38,20 +38,20 @@
|
||||
{{ form_row(form.plainPassword.second, {'label': 'Répéter le mot de passe'}) }}
|
||||
</div>
|
||||
</div>
|
||||
{% if destination_login is defined %}
|
||||
<input type="hidden" name="_target_path" value="{{ destination_login }}" />
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{% set label = 'user.cgu.label'|trans({'%link_start%' : '<a href="'~path('service_cgu')~'">', '%link_end%' : '</a>'}, 'messages') | raw %}
|
||||
{{ form_errors(form.cgu) }}
|
||||
{{ form_widget(form.cgu) }}
|
||||
{{ form_label(form.cgu) }}
|
||||
<br />
|
||||
{#{{ form_widget(form.cgu) }}
|
||||
{{ form_label(form.cgu) }}#}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<a href="{{ path('service_cgu') }}" title="conditions d'utilisations" style="margin-left:45px;">Lire les conditions générales d'utilisations</a>
|
||||
Lire les conditions générales d'utilisations</a>
|
||||
<br /><br />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+71
-9
@@ -1,16 +1,78 @@
|
||||
{% block fos_user_content %}
|
||||
<div class="row">
|
||||
<div class="col s12 m12 l6 col-login-first col-login">
|
||||
<h4>Connexion</h4>
|
||||
<div class="row login_face active_face">
|
||||
<div class="col s12 col-login-first col-login">
|
||||
<h4 class="color-blue1 pull-left">Connexion</h4>
|
||||
<div class="pull-right">
|
||||
ou
|
||||
<a href="#" class="color-blue1 change_to_register">
|
||||
Créer un compte
|
||||
</a>
|
||||
</div>
|
||||
<br class="clearfix"/>
|
||||
{{ include('@FOSUser/Security/login_content.html.twig') }}
|
||||
</div>
|
||||
<div class="col s12 m12 l6 col-login-first col-login-subscribe hide-on-med-and-down">
|
||||
<div class="center-align">
|
||||
<strong>Je ne possède pas de<br />compte Talents Tube</strong>
|
||||
</div>
|
||||
<div class="row register_face">
|
||||
<div class="col s12 col-login-first col-login">
|
||||
<h4 class="color-blue1 pull-left">Créer un compte</h4>
|
||||
<div class="pull-right">
|
||||
ou
|
||||
<a href="#" class="color-blue1 change_to_login">
|
||||
Se connecter
|
||||
</a>
|
||||
</div>
|
||||
<a href="{{ path('fos_user_registration_register') }}" class="btn btn-default btn-round">
|
||||
Créer un compte
|
||||
</a>
|
||||
<br class="clearfix"/>
|
||||
{{ form_start(register_form, {'method': 'post', 'action': path('fos_user_registration_register'), 'attr': {'class': 'fos_user_registration_register'}}) }}
|
||||
{{ form_errors(register_form) }}
|
||||
<div class="row">
|
||||
<div class="input-field col s6">
|
||||
{{ form_row(register_form.firstname, {'label': 'Prénom'}) }}
|
||||
</div>
|
||||
<div class="input-field col s6">
|
||||
{{ form_row(register_form.lastname, {'label': 'Nom'}) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{{ form_row(register_form.email, {'label': 'E-mail'}) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hide">
|
||||
{{ form_row(register_form.username, {'label': 'Username'}) }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{{ form_row(register_form.plainPassword.first, {'label': 'Mot de passe'}) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{{ form_row(register_form.plainPassword.second, {'label': 'Répéter le mot de passe'}) }}
|
||||
</div>
|
||||
</div>
|
||||
{% if destination_login is defined %}
|
||||
<input type="hidden" name="_target_path" value="{{ destination_login }}" />
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{% set label = 'user.cgu.label'|trans({'%link_start%' : '<a style="margin-bottom:25px;" target="_blank" href="'~path('service_cgu')~'">', '%link_end%' : '</a>'}, 'messages') | raw %}
|
||||
{{ form_errors(register_form.cgu) }}
|
||||
{{ form_widget(register_form.cgu) }}
|
||||
{% block form_label %}
|
||||
{% spaceless %}
|
||||
<label for="fos_user_registration_form_cgu">
|
||||
Je déclare avoir pris connaissance et accepté les termes et les <a target="_blank" href="{{ path("service_cgu") }}">conditions générales d’utilisation</a>
|
||||
</label>
|
||||
{% endspaceless %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-30">
|
||||
<div class="col s12 center-align">
|
||||
<input class="btn btn-default btn-round" type="submit" id="_submit_register" name="_submit_register" value="Créer un compte" />
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(register_form) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock fos_user_content %}
|
||||
|
||||
+19
-23
@@ -1,5 +1,6 @@
|
||||
{% trans_default_domain 'FOSUserBundle' %}
|
||||
|
||||
{% include 'default/facebook.html.twig' %}
|
||||
{% if error %}
|
||||
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
|
||||
{% endif %}
|
||||
@@ -7,38 +8,33 @@
|
||||
{% if csrf_token %}
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token }}" />
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="row field-login">
|
||||
<div class="input-field col s12">
|
||||
<label for="username">{{ 'security.login.username'|trans }}</label>
|
||||
<label for="username">e-mail</label>
|
||||
<input type="text" id="username" name="_username" value="{{ last_username }}" required="required" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row field-login">
|
||||
<div class="input-field col s12">
|
||||
<label for="password">{{ 'security.login.password'|trans }}</label>
|
||||
<input type="password" id="password" name="_password" required="required" />
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<input type="checkbox" id="remember_me" name="_remember_me" value="on" />
|
||||
<label for="remember_me">{{ 'security.login.remember_me'|trans }}</label>
|
||||
</p>
|
||||
<br />
|
||||
<div class="row hide-on-large-only">
|
||||
<div class="col s6">
|
||||
<input class="btn btn-default btn-round" type="submit" id="_submit" name="_submit" value="{{ 'security.login.submit'|trans }}" /><br />
|
||||
<a href="{{path('fos_user_resetting_request')}}"><span class="tiny-text font-black m-l-10">J'ai oublié mon mot de passe</span></a>
|
||||
</div>
|
||||
<div class="col s6">
|
||||
<a href="{{ path('fos_user_registration_register') }}" class="btn btn-default btn-round">
|
||||
Créer un compte
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row hide-on-med-and-down">
|
||||
<div class="row field-login-checkbox">
|
||||
<div class="col s12">
|
||||
<input class="btn btn-default btn-round" type="submit" id="_submit" name="_submit" value="{{ 'security.login.submit'|trans }}" /><br />
|
||||
<a href="{{path('fos_user_resetting_request')}}"><span class="tiny-text font-black m-l-10">J'ai oublié mon mot de passe</span></a>
|
||||
<input type="checkbox" id="remember_me" name="_remember_me" value="on" />
|
||||
<label for="remember_me">{{ 'security.login.remember_me'|trans }}</label>
|
||||
<br class="clearfix"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br />
|
||||
{% if destination_login is defined %}
|
||||
<input type="hidden" name="_target_path" value="{{ destination_login }}" />
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col s12 center-align">
|
||||
<input class="btn btn-default btn-round" type="submit" id="_submit" name="_submit" value="Se connecter" /><br />
|
||||
<a href="{{path('fos_user_resetting_request')}}"><span class="tiny-text font-black m-l-10">Mot de passe oublié ?</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,98 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% block content %}
|
||||
{% if typeObject == 'Ad' %}
|
||||
{% set subtitle = 'Candidature à l\'offre d\'emploi:' %}
|
||||
{% set title = object.title %}
|
||||
{% set route = "answer_create_ad_step2" %}
|
||||
{% elseif typeObject == 'Company' %}
|
||||
{% set subtitle = 'Candidature pour l\'entreprise :' %}
|
||||
{% set title = object.brandName %}
|
||||
{% set route = "answer_create_company_step2" %}
|
||||
{% endif %}
|
||||
<div class="fluid-container bg-grey-3">
|
||||
<div class="container contentWrapper contentPage" id="answerPage">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="center-align bg-grey-3 m-t-20">
|
||||
<strong>{{ subtitle }}</strong>
|
||||
<h3 class="color-blue1 m-t-10">{{ title }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="col-content bg-arrow z-depth-2 p-20">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h4 class="color-blue1 p-0"><span class="title-bubble">1</span> Importez votre CV Vidéo</h4>
|
||||
</div>
|
||||
<div class="col s10 offset-s1 answer-dropzone">
|
||||
{% include 'media/dropzone.html.twig' with {'deleting': true } %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_start(form, {'attr': { 'id': 'form_media' } }) }}
|
||||
<div class="col-content z-depth-2 p-20 m-t-40">
|
||||
{{ form_row(form.status) }}
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h4 class="color-blue1 p-0"><span class="title-bubble">2</span> Ajoutez votre commentaire</h4>
|
||||
</div>
|
||||
<div class="col s10 offset-s1">
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{{ form_errors(form.comment) }}
|
||||
{{ form_widget(form.comment) }}
|
||||
{{ form_label(form.comment) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_rest(form) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-40">
|
||||
<div class="col s10 offset-s1 center-align">
|
||||
<button id="submit" class=" btn btn-large btn-default disabled" type="submit">
|
||||
Envoyer ma candidature
|
||||
</button><br /><br />
|
||||
<a class="color-grey5" href="{{ path(routeObject,{'alias': object.alias }) }}">Annuler</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
</div>
|
||||
{% include 'answer/answer_tuto.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
var routeComment = '{{ path("anwser_edit_comment", {"id": answer.getId()}) }}';
|
||||
var urlDropzone = '{{ path("upload_media_answer", {"id": answer.getId()}) }}';
|
||||
var urlDelete = '{{ path("delete_media_answer", {"id": answer.getId() }) }}'
|
||||
var idObject = '{{ answer.getId() }}';
|
||||
var typeObject = "answer";
|
||||
{% if video %}
|
||||
if($("#form_media").length){
|
||||
$("#submit").removeClass('disabled');
|
||||
$("#submit").addClass('pulse');
|
||||
}
|
||||
var videoExist = true;
|
||||
var videoOriginal = "{{video.getOriginalUrl()}}";
|
||||
var mockFile = { name: '{{video.getTitle()}}', size: '181818181', type: 'mp4', serverID: 0, accepted: true };
|
||||
{% else %}
|
||||
var videoExist = false;
|
||||
{% endif %}
|
||||
</script>
|
||||
<script src="{{ asset('/js/dropzone.js') }}"></script>
|
||||
<script src="{{ asset('/js/answer.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
{% if mustLogin == true %}
|
||||
$('#submit').on('click',function(event){
|
||||
event.preventDefault();
|
||||
$('#modal_login').modal('open');
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,55 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% block content %}
|
||||
{% if typeObject == 'Ad' %}
|
||||
{% set subtitle = 'Candidature à l\'offre d\'emploi:' %}
|
||||
{% set title = object.title %}
|
||||
{% set route = "answer_create_ad_step2" %}
|
||||
{% elseif typeObject == 'Company' %}
|
||||
{% set subtitle = 'Candidature pour l\'entreprise :' %}
|
||||
{% set title = object.brandName %}
|
||||
{% set route = "answer_create_company_step2" %}
|
||||
{% endif %}
|
||||
|
||||
<div class="fluid-container bg-grey-3">
|
||||
<div class="container contentWrapper contentPage">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="col-content center-align m-t-20">
|
||||
<strong>{{ subtitle }}</strong>
|
||||
<h3 class="color-blue1 m-0 m-t-10">{{ title }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s12 m6 ">
|
||||
<div class="col-content p-15 bg-blue-1 center-align whiteColor z-depth-2 choice-answer-item">
|
||||
<img src="{{ asset('img/img_popupcv/webcam_cv_video.svg')}}">
|
||||
<h3>J'enregistre mon CV vidéo !</h3>
|
||||
<p>Suivez nos conseils puis enregistrez votre CV vidéo directement depuis votre webcam.</p>
|
||||
<a href="{{ path(route,{'id': object.id,'type': 'webcam' }) }}" class="btn btn-default white">Enregistrer mon CV vidéo</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s12 m6">
|
||||
<div class="col-content p-15 center-align z-depth-2 choice-answer-item bg-white">
|
||||
<img src="{{ asset('img/img_popupcv/videotheque_cv_video.svg')}}">
|
||||
<h3>Je possède un CV vidéo !</h3>
|
||||
<p>Importez une vidéo ou séléctionnez une vidéo<br /> dans votre vidéothèque</p>
|
||||
<a href="{{ path(route,{'id': object.id, 'type': 'import'}) }}" class="btn btn btn-default">Importer mon CV vidéo</a><br /><br />
|
||||
<a id="link-videotheque" href="{{ route_videotheque }}">Choisir une vidéo de ma vidéothèque</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
{% if mustLogin == true %}
|
||||
$('#link-videotheque').on('click',function(event){
|
||||
event.preventDefault();
|
||||
$('#modal_login').modal('open');
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,131 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% block content %}
|
||||
{% if typeObject == 'Ad' %}
|
||||
{% set subtitle = 'Candidature à l\'offre d\'emploi:' %}
|
||||
{% set title = object.title %}
|
||||
{% set route = "answer_create_ad_step2" %}
|
||||
{% elseif typeObject == 'Company' %}
|
||||
{% set subtitle = 'Candidature pour l\'entreprise :' %}
|
||||
{% set title = object.brandName %}
|
||||
{% set route = "answer_create_company_step2" %}
|
||||
{% endif %}
|
||||
<div class="fluid-container bg-grey-3">
|
||||
<div class="container contentWrapper contentPage" id="answerPage">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="center-align bg-grey-3 m-t-20">
|
||||
<strong>{{ subtitle }}</strong>
|
||||
<h3 class="color-blue1 m-t-10">{{ title }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="col-content bg-arrow z-depth-2 p-20">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h4 class="color-blue1 p-0"><span class="title-bubble">1</span> Selectionnez votre CV vidéo</h4>
|
||||
</div>
|
||||
<div class="col s12">
|
||||
<div class="row">
|
||||
<div class="col s10 offset-s1">
|
||||
<div class="p-30">
|
||||
{% for vid in videos %}
|
||||
{% set img = "" %}
|
||||
{% if vid.thumbnails.0 is defined %}
|
||||
{% set img = vid.thumbnails.0.sizes.4.link %}
|
||||
{% endif %}
|
||||
<div class="col s4">
|
||||
<div class="card">
|
||||
<div class="card-image">
|
||||
<div class="embed-responsive embed-responsive-16by9">
|
||||
<iframe src="https://player.vimeo.com{{ vid.getCdnUrl()|replace({ "videos": "video"}) }}" width="100%" height="auto" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<div style="cursor:pointer;" class="card-action" data-url="https://player.vimeo.com{{ vid.getCdnUrl()|replace({ "videos": "video"}) }}" data-img="{{ img }}" data-position="top" data-tooltip="Choisir cette vidéo" href="#" data-title="{{ vid.getTitle() }}" data-id="{{ vid.getId() }}">
|
||||
<a class="addExistingVideo" >
|
||||
<span>{{ vid.getTitle() }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_start(form, {'attr': { 'id': 'form_media' } }) }}
|
||||
<div class="col-content z-depth-2 p-20 m-t-40">
|
||||
{{ form_row(form.status) }}
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h4 class="color-blue1 p-0"><span class="title-bubble">2</span> Ajoutez votre commentaire</h4>
|
||||
</div>
|
||||
<div class="col s10 offset-s1">
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{{ form_errors(form.comment) }}
|
||||
{{ form_widget(form.comment) }}
|
||||
{{ form_label(form.comment) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_rest(form) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-40">
|
||||
<div class="col s10 offset-s1 center-align">
|
||||
<button id="submit" class=" btn btn-large btn-default disabled" type="submit">
|
||||
Envoyer ma candidature
|
||||
</button><br /><br />
|
||||
<a class="color-grey5" href="{{ path(routeObject,{'alias': object.alias }) }}">Annuler</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
</div>
|
||||
{% include 'answer/answer_tuto.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script src="{{ asset('/js/answer.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
{% if mustLogin == true %}
|
||||
$('#submit').on('click',function(event){
|
||||
event.preventDefault();
|
||||
$('#modal_login').modal('open');
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
$(".card-action").click(function(){
|
||||
var idVideo = $(this).data("id");
|
||||
var titleVideo = $(this).data("title");
|
||||
var imgVideo = $(this).data("img");
|
||||
var urlVideo = $(this).data("url");
|
||||
var elem = $(this);
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '{{ path("link_media_answer", {"id": answer.getId()}) }}',
|
||||
data: "idVideo="+idVideo,
|
||||
dataType: 'html',
|
||||
success: function (response) {
|
||||
$(".card-action").removeClass('active');
|
||||
elem.addClass('active');
|
||||
Materialize.toast('Vidéo bien associé', 4000);
|
||||
if($("#submit").hasClass('disabled')){
|
||||
$("#submit").removeClass('disabled');
|
||||
$("#submit").addClass('pulse');
|
||||
}
|
||||
},
|
||||
error: function (response, xhr, ajaxOptions, thrownError) {
|
||||
Materialize.toast('Erreur lors de l\'association de la vidéo!', 4000);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,138 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% block content %}
|
||||
{% if typeObject == 'Ad' %}
|
||||
{% set subtitle = 'Candidature à l\'offre d\'emploi:' %}
|
||||
{% set title = object.title %}
|
||||
{% set route = "answer_create_ad_step2" %}
|
||||
{% elseif typeObject == 'Company' %}
|
||||
{% set subtitle = 'Candidature pour l\'entreprise :' %}
|
||||
{% set title = object.brandName %}
|
||||
{% set route = "answer_create_company_step2" %}
|
||||
{% endif %}
|
||||
<div class="fluid-container bg-grey-3">
|
||||
<div class="container contentWrapper contentPage" id="answerPage">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="center-align bg-grey-3 m-t-20">
|
||||
<strong>{{ subtitle }}</strong>
|
||||
<h3 class="color-blue1 m-t-10">{{ title }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="col-content bg-arrow z-depth-2 p-20">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h4 class="color-blue1 p-0"><span class="title-bubble">1</span> Enregistrez votre cv vidéo</h4>
|
||||
</div>
|
||||
<div class="col m4 offset-m1 s12">
|
||||
<video id="myCvVideo" class="video-js vjs-default-skin"></video>
|
||||
<div class="video-informations-card center-align">
|
||||
<p>Merci de cliquer sur l'icône <span class="vjs-icon-device-perm"></span> afin d'activer votre webcam.</p>
|
||||
<p>Votre webcam activée, lancez l'enregistrement en cliquant sur le bouton rond en bas à gauche du lecteur.</p>
|
||||
<p>Si vous n'êtes pas satisfait(e) de votre CV vidéo après un enregistrement, vous pouvez à tout moment recommencer l'enregistrement.</p>
|
||||
<p><a class="view-tuto">Revoir le tutoriel</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col m2 hide-on-small-only">
|
||||
<div class="valign-wrapper" style="align-items:center;justify-content: center;height: 300px;width:100%;">
|
||||
<div style="font-size:40px;">
|
||||
<i class="fa fa-arrow-right center-align" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col m4 s12">
|
||||
<video id="preview" controls="true">
|
||||
<span class="vjs-control-text">Play Video</span>
|
||||
</video>
|
||||
<div class="btnContainer center-align" style="display:none;">
|
||||
<a id="saveRecord" href="#!" class="saveVideo btn btn-default">
|
||||
<i class="fa fa-floppy-o" aria-hidden="true"></i> Télécharger mon CV Vidéo
|
||||
</a>
|
||||
<div class="video-informations-card">
|
||||
<p>Une fois téléchargé vous pouvez glisser-déposer votre CV vidéo dans la zone prévue pour lancer le téléchargement.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-content bg-arrow z-depth-2 p-20">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h4 class="color-blue1 p-0"><span class="title-bubble">2</span> Importez votre CV Vidéo</h4>
|
||||
</div>
|
||||
<div class="col s10 offset-s1 answer-dropzone">
|
||||
{% include 'media/dropzone.html.twig' with {'deleting': true } %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_start(form, {'attr': { 'id': 'form_media' } }) }}
|
||||
<div class="col-content z-depth-2 p-20 m-t-40">
|
||||
{{ form_row(form.status) }}
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h4 class="color-blue1 p-0"><span class="title-bubble">3</span> Ajoutez votre commentaire</h4>
|
||||
</div>
|
||||
<div class="col s10 offset-s1">
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{{ form_errors(form.comment) }}
|
||||
{{ form_widget(form.comment) }}
|
||||
{{ form_label(form.comment) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_rest(form) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-40">
|
||||
<div class="col s10 offset-s1 center-align">
|
||||
<button id="submit" class=" btn btn-large btn-default disabled" type="submit">
|
||||
Envoyer ma candidature
|
||||
</button><br /><br />
|
||||
<a class="color-grey5" href="{{ path(routeObject,{'alias': object.alias }) }}">Annuler</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
</div>
|
||||
{% include 'answer/answer_tuto.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
var routeComment = '{{ path("anwser_edit_comment", {"id": answer.getId()}) }}';
|
||||
var urlDropzone = '{{ path("upload_media_answer", {"id": answer.getId()}) }}';
|
||||
var urlDelete = '{{ path("delete_media_answer", {"id": answer.getId() }) }}'
|
||||
var idObject = '{{ answer.getId() }}';
|
||||
var typeObject = "answer";
|
||||
{% if video %}
|
||||
if($("#form_media").length){
|
||||
$("#submit").removeClass('disabled');
|
||||
$("#submit").addClass('pulse');
|
||||
}
|
||||
var videoExist = true;
|
||||
var videoOriginal = "{{video.getOriginalUrl()}}";
|
||||
var mockFile = { name: '{{video.getTitle()}}', size: '181818181', type: 'mp4', serverID: 0, accepted: true };
|
||||
{% else %}
|
||||
var videoExist = false;
|
||||
{% endif %}
|
||||
</script>
|
||||
<script src="{{ asset('/js/dropzone.js') }}"></script>
|
||||
<script src="{{ asset('/js/vendors/video.min.js') }}"></script>
|
||||
<script src="{{ asset('/js/vendors/RecordRTC.js') }}"></script>
|
||||
<script src="{{ asset('/js/vendors/videojs.record.js') }}"></script>
|
||||
<script src="{{ asset('/js/vendors/jquery.browser.min.js') }}"></script>
|
||||
<script src="{{ asset('/js/answer.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
{% if mustLogin == true %}
|
||||
$('#submit').on('click',function(event){
|
||||
event.preventDefault();
|
||||
$('#modal_login').modal('open');
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,84 @@
|
||||
<div id="modal-tuto" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="col-content">
|
||||
<h3 class="color-blue1 center-align">Les conseils de Talents Tube</h3>
|
||||
<div class="owl-carousel tuto-carousel">
|
||||
<div class="item-tuto-webcam carousel-item">
|
||||
<div class="row">
|
||||
<div class="col s4 offset-s1">
|
||||
<img class="responsive-img" src="{{ asset('img/img_popupcv/cv_video_step_1.svg')}}" alt="">
|
||||
</div>
|
||||
<div class="col s6 offset-s1 left-align">
|
||||
<strong><span class="color-blue1">30 Minutes</span> avant l’enregistrement</strong>
|
||||
<br /><br />
|
||||
<u>Préparez un discours <strong>clair</strong> et <strong>concis</strong></u>
|
||||
<br /><br />
|
||||
<strong>Posez-vous les bonnes questions : <br />
|
||||
<ul>
|
||||
<li>Quelle est ma situation ? </li>
|
||||
<li>Quelles sont mes compétences ?</li>
|
||||
<li>Que puis-je apporter à l’entreprise? </li>
|
||||
<li>Pourquoi je souhaite rejoindre l’entreprise?</li>
|
||||
</ul>
|
||||
</strong>
|
||||
Une fois votre scénario terminé, <strong>répétez-le plusieurs fois à voix haute pour maîtriser votre sujet.</strong>
|
||||
<br /><br />
|
||||
N’oubliez pas qu’en vidéo, <strong>il faut rester soi même !</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-tuto-webcam carousel-item">
|
||||
<div class="row">
|
||||
<div class="col s4 offset-s1">
|
||||
<img class="responsive-img" src="{{ asset('img/img_popupcv/cv_video_step_2.svg')}}" alt="">
|
||||
</div>
|
||||
<div class="col s6 offset-s1 left-align">
|
||||
<strong><span class="color-blue1">2 Minutes</span> avant l’enregistrement</strong>
|
||||
<br /><br />
|
||||
<u>Préparer votre lieu de tournage :</u>
|
||||
<br /><br />
|
||||
<ul>
|
||||
<li>Privilégiez un fond <strong>neutre</strong> et <strong>clair</strong></li>
|
||||
<li>Trouvez un lieu <strong>peu bruyant</strong></li>
|
||||
<li>Installez-vous dans un endroit <strong>bien éclairé de préférence naturel, sans contre jour</strong></li>
|
||||
<li>Portez des <strong>habits de couleurs neutres et évitez les rayures, les pois et les carreaux.</strong></li>
|
||||
<li>Privilégiez un <strong>cadrage au niveau du buste</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-tuto-webcam carousel-item">
|
||||
<div class="row">
|
||||
<div class="col s4 offset-s1">
|
||||
<img class="responsive-img" src="{{ asset('img/img_popupcv/cv_video_step_3.svg')}}" alt="">
|
||||
</div>
|
||||
<div class="col s6 offset-s1 left-align">
|
||||
<strong><span class="color-blue1">10 secondes</span> avant l’enregistrement</strong>
|
||||
<br /><br />
|
||||
<u>Quelques informations à savoir avant le grand bain :</u>
|
||||
<br /><br />
|
||||
<strong>
|
||||
<ul>
|
||||
<li>Le CV doit durer entre 1.30 et 2 minutes.</li>
|
||||
<li>Pensez à bien articuler.</li>
|
||||
<li>Évitez les euuuh et les baaah !</li>
|
||||
<li>Recommencez autant de fois que nécessaire.</li>
|
||||
</ul>
|
||||
<br /><br />
|
||||
Bon courage !
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-fixed-item right">
|
||||
<a class="text close-tuto">Passer l'intro</a>
|
||||
<a class="btn btn-default next-slide">suivant</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -58,6 +58,7 @@
|
||||
<link rel="stylesheet" href="{{ asset('css/styles.css') }}">
|
||||
{% block stylesheets %}
|
||||
{% endblock %}
|
||||
<script type="text/javascript"> window.$crisp=[];window.CRISP_WEBSITE_ID="78ec5756-1cd5-4f3f-b0db-cb408c8171da";(function(){ d=document;s=d.createElement("script"); s.src="https://client.crisp.chat/l.js"; s.async=1;d.getElementsByTagName("head")[0].appendChild(s);})(); </script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
|
||||
{% block dashContent %}
|
||||
<div class="row bg-bordered p-20">
|
||||
{% include 'company/tabs.html.twig' with {'tab':2} %}
|
||||
|
||||
{% if type == "spontaneous" %}
|
||||
{% include 'company/tabs.html.twig' with {'tab':3} %}
|
||||
{% else %}
|
||||
{% include 'company/tabs.html.twig' with {'tab':2} %}
|
||||
{% endif %}
|
||||
|
||||
<div id="reponses" class="col s12">
|
||||
<table id="table-answers" class="bordered highlight responsive-table">
|
||||
<thead>
|
||||
@@ -20,7 +26,7 @@
|
||||
<td>
|
||||
<a href="{{ path('company_answer_show',{'id':answer.id }) }}">{{ answer.user.firstname }} {{ answer.user.lastname }}</a>
|
||||
</td>
|
||||
<td>{{ answer.ad.title }}</td>
|
||||
<td>{% if answer.ad != null %}{{ answer.ad.title }} {% endif %}</td>
|
||||
<td>{{ answer.cTime()|date('d-m-Y') }}</td>
|
||||
<td>
|
||||
{#{% if answer.notes|length <= 0 %}#}
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
{% block dashContent %}
|
||||
<div class="row bg-bordered p-20">
|
||||
<h1 class="alttitle">Réponse de {{ answer.user.firstname }} {{ answer.user.lastname }}</h1>
|
||||
{% include 'company/tabs.html.twig' with {'tab':2} %}
|
||||
{% if type == "spontaneous" %}
|
||||
{% include 'company/tabs.html.twig' with {'tab':3} %}
|
||||
{% else %}
|
||||
{% include 'company/tabs.html.twig' with {'tab':2} %}
|
||||
{% endif %}
|
||||
|
||||
<div class="col s12 m6 l6 col-padding-right">
|
||||
<div class="bg-bordered inside-m">
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% include 'default/flash_message.html.twig' %}
|
||||
<div class="fluid-container bg-grey-3">
|
||||
<div class="container">
|
||||
<div id="detail-offre-video" class="row">
|
||||
@@ -89,6 +90,16 @@
|
||||
<p class="chip">{{ tag }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="col s12 m-t-20 ">
|
||||
{% if not is_granted('ROLE_COMPANY') %}
|
||||
<p class="item-detail-reply center-align">
|
||||
<a href="{{ path('talent_spontaneous_application_create',{'id': company.id}) }}"
|
||||
class="btn btn-default">
|
||||
Candidature spontanée
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="offers" class="row">
|
||||
@@ -107,6 +118,37 @@
|
||||
{% if company.territories|length %}
|
||||
{% include 'ad/carrousel_territory.html.twig' with {'territories': company.territories } %}
|
||||
{% endif %}
|
||||
|
||||
{% set popup_answer_send = app.session.flashbag.get('popup_answer_send') %}
|
||||
{% set popup_answer_already_send = app.session.flashbag.get('popup_answer_already_send') %}
|
||||
{% if popup_answer_send is not empty %}
|
||||
{% if popup_answer_send[0] == true %}
|
||||
<div id="modal-answer" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="col-content p-30">
|
||||
{% if popup_answer_already_send is not empty and popup_answer_already_send[0] == true %}
|
||||
<h3 class="color-blue1 center-align">Votre candidature a bien été envoyée à {{ company.brandname }} !</h3>
|
||||
<div class="col offset-s2 s8 center-align">
|
||||
<div class="embed-responsive embed-responsive-16by9">
|
||||
<div class="col s12">
|
||||
<iframe src="https://player.vimeo.com/video/249822507"
|
||||
width="100%" height="640" frameborder="0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ path('talent_dashboard') }}" class="btn btn-default m-t-20">Accéder à mon profil</a>
|
||||
<br /><br />
|
||||
<a class="text modal-action modal-close">Fermer</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -118,8 +160,14 @@
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
console.log("passe");
|
||||
$.get("{{ path('company_ajaxRecStat',{'id': company.id }) }}", function (data) {});
|
||||
if ($('#modal-answer').length > 0) {
|
||||
$('#modal-answer').modal({
|
||||
dismissible: false,
|
||||
ready: function(modal, trigger) {}
|
||||
});
|
||||
$('#modal-answer').modal('open');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,18 @@
|
||||
<ul class="tabs">
|
||||
<li class="tab col s6"><a {% if tab == 1 %}class="active"{% endif %} target="_self" href="{{ path('company_ad') }}">{{ 'company.menu.ads.label'|trans }}</a></li>
|
||||
<li class="tab col s6"><a {% if tab == 2 %}class="active"{% endif %} target="_self" href="{{ path('company_answer_list') }}">{{ 'company.menu.answers.label'|trans }}</a></li>
|
||||
{#<li class="tab col s4"><a {% if tab == 3 %}class="active"{% endif %} href="#favoris">{{ 'company.menu.bookmark.label'|trans }}</a></li>#}
|
||||
<li class="tab col s4">
|
||||
<a {% if tab == 1 %}class="active"{% endif %} target="_self"
|
||||
href="{{ path('company_ad') }}">
|
||||
{{ 'company.menu.ads.label'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab col s4">
|
||||
<a {% if tab == 2 %}class="active"{% endif %} target="_self" href="{{ path('company_answer_list') }}">
|
||||
{{ 'company.menu.answers.label'|trans }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab col s4">
|
||||
<a {% if tab == 3 %}class="active"{% endif %} target="_self" href="{{ path('company_spontaneous_list') }}">
|
||||
Candidatures spontanées
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}{{ ad.htmlTitle|raw }}{% endblock %}
|
||||
|
||||
@@ -124,6 +124,44 @@
|
||||
{% elseif ad.company.territories|length %}
|
||||
{% include 'ad/carrousel_territory.html.twig' with {'territories': ad.company.territories } %}
|
||||
{% endif %}
|
||||
{% set popup_answer_send = app.session.flashbag.get('popup_answer_send') %}
|
||||
{% set popup_answer_already_send = app.session.flashbag.get('popup_answer_already_send') %}
|
||||
{% if popup_answer_send is not empty %}
|
||||
{% if popup_answer_send[0] == true %}
|
||||
<div id="modal-answer" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="col-content p-30">
|
||||
{% if popup_answer_already_send is not empty and popup_answer_already_send[0] == true %}
|
||||
<h3 class="color-blue1 center-align">Candidature pour {{ ad.company.brandname }} !</h3>
|
||||
<div class="col offset-s2 s8 center-align">
|
||||
<p>Vous avez déjà répondu à cette annonce.</p>
|
||||
<a href="{{ path('talent_dashboard') }}" class="btn btn-default m-t-20">Accéder à mon profil</a>
|
||||
<br /><br />
|
||||
<a class="text modal-action modal-close">Fermer</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<h3 class="color-blue1 center-align">Votre candidature a bien été envoyée à {{ ad.company.brandname }} !</h3>
|
||||
<div class="col offset-s2 s8 center-align">
|
||||
<div class="embed-responsive embed-responsive-16by9">
|
||||
<div class="col s12">
|
||||
<iframe src="https://player.vimeo.com/video/249822507"
|
||||
width="100%" height="640" frameborder="0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ path('talent_dashboard') }}" class="btn btn-default m-t-20">Accéder à mon profil</a>
|
||||
<br /><br />
|
||||
<a class="text modal-action modal-close">Fermer</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
@@ -133,15 +171,19 @@
|
||||
$(document).ready(function(){
|
||||
$('.tooltipped').tooltip();
|
||||
$.get( "{{ path('ad_ajaxRecStat',{'id': ad.id }) }}", function( data ) {});
|
||||
if ($('#modal-answer').length > 0) {
|
||||
$('#modal-answer').modal({
|
||||
dismissible: false,
|
||||
ready: function(modal, trigger) {}
|
||||
});
|
||||
$('#modal-answer').modal('open');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
|
||||
<script type="application/ld+json">
|
||||
{{- ad.structuredData|raw -}}
|
||||
{{- ad.structuredData|raw -}}
|
||||
</script>
|
||||
<script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5a095d2dd3cf90df"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% if not app.user %}
|
||||
{% if not app.user or is_granted('ROLE_TALENT_TMP')%}
|
||||
<div class="header-link header-link-signin right-align">
|
||||
{% if app.request.get('_route') == "fos_user_security_login" %}
|
||||
{% set href = "" %}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{% if destination_login is defined %}
|
||||
{% set destination = destination_login %}
|
||||
{% else %}
|
||||
{% set destination = path('redirect_dashboard') %}
|
||||
{% endif %}
|
||||
<a class="btn btn-default btn-facebook z-depth-2" href="{{ url("hwi_oauth_service_redirect", {service: "facebook"}) }}?_destination={{ destination }}">Connexion avec Facebook</a>
|
||||
<a class="btn btn-default btn-linkedin z-depth-2" href="{{ url("hwi_oauth_service_redirect", {service: "linkedin"}) }}?_destination={{ destination }}">Connexion avec Linkedin</a>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<div class="separator-with-label">
|
||||
<hr>
|
||||
<label><span>ou</span></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,4 +70,4 @@
|
||||
{{ render(controller('AppBundle:SecurityModal:loginModal')) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -10,11 +10,16 @@
|
||||
<div class="col l12 m12 s12 home-content col-noPadding">
|
||||
<div id="homeTeaser" class="valign-wrapper">
|
||||
<h1 class="white-text center-align">1er job board <span>100% vidéo</span></h1><br />
|
||||
<a data-target="modal_concept" href="#!" class="btn btn-default btn-round btn-concept modal-trigger">
|
||||
Le concept en vidéo
|
||||
<img src="{{asset('img/bouton-play.png')}}" />
|
||||
</a>
|
||||
|
||||
<div class="link-home-head">
|
||||
<a data-target="modal_concept" href="#!" class="btn btn-default btn-round btn-concept modal-trigger">
|
||||
Le concept en vidéo
|
||||
<img src="{{asset('img/bouton-play.png')}}" />
|
||||
</a>
|
||||
<a data-target="modal_voeux" href="#!" class="btn btn-default btn-round btn-voeux modal-trigger">
|
||||
Voeux 2018 en vidéo
|
||||
<img src="{{asset('img/bouton-play.png')}}" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-video-inner">
|
||||
<video loop="" preload="metadata" autoplay="" id="vid" muted>
|
||||
<source src="/img/home_video.mp4" type="video/mp4">
|
||||
@@ -210,6 +215,17 @@
|
||||
<a href="#!" class="modal-action modal-close btn btn-default button-round-small">Fermer</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal_voeux" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<div class="video-container">
|
||||
<iframe id="iframe_concept" src="" frameborder="0" webkitallowfullscreen mozallowfullscreen
|
||||
allowfullscreen></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="modal-action modal-close btn btn-default button-round-small">Fermer</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
<div class="item">
|
||||
<a href="{{ path('talent_show',{'alias': talent.alias }) }}"
|
||||
title="{{ user.firstname }} {{ user.lastname }}">
|
||||
{% if user.photo %}
|
||||
{% if user.socialPicture is not null %}
|
||||
<img class="circle z-depth-3" src="{{ user.socialPicture }}" alt="{{ user.firstname }} {{ user.lastname }}"/>
|
||||
{% elseif user.photo %}
|
||||
<img class="circle z-depth-3" src="{{ user.webPath | imagine_filter('profile_square') }}" alt="{{ user.firstname }} {{ user.lastname }}"/>
|
||||
{% else %}
|
||||
<img class="circle z-depth-3"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="dz-error-mark"><span>✘</span></div>
|
||||
<div class="dz-error-message"><span data-dz-errormessage></span></div>
|
||||
{% if deleting is defined %}
|
||||
<a href="javascript:undefined;" class="btn btn-default bnt-round dz-remove" data-dz-remove>Supprimer la vidéo</a>
|
||||
<a href="javascript:undefined;" class="dz-remove" data-dz-remove>Supprimer la vidéo</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
var urlDelete = '';
|
||||
var idObject = null;
|
||||
var typeObject = null;
|
||||
var videoExist = false;
|
||||
var videoExist = true;
|
||||
var mediaDefaultUrl = '{{ path("media_default") }}';
|
||||
var idUser = '{{ app.user.id }}';
|
||||
var mediaEditTitleUrl = "{{ path('media_edit_title', {'id': 'id_video'})|escape('js') }}";
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{#<h1 class="alttitle">Espace offres d'emploi</h1>#}
|
||||
<div class=" row bg-bordered p-20">
|
||||
<ul class="tabs">
|
||||
<li class="tab col s4"><a class="active" href="#reponses">Mes réponses</a></li>
|
||||
<li class="tab col s4"><a class="active" href="#reponses">Mes candidatures</a></li>
|
||||
</ul>
|
||||
<div id="reponses" class="col s12">
|
||||
<table class="bordered highlight responsive-table">
|
||||
@@ -21,15 +21,18 @@
|
||||
{% for a in answers %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ a.ad.title }}<br />
|
||||
{% if a.ad != null %}{{ a.ad.title }}{% else %}{{ a.company.brandname }}{% endif %}<br />
|
||||
{% if a.video is not null %}
|
||||
<a class="waves-effect waves-light btn btn-default modalBtn" href="#" data-url="{{ a.video.cdnUrl }}">Voir la vidéo</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ a.uTime|date('d/m/Y') }}</td>
|
||||
<td>{{ a.ad.countAnswers }}</td>
|
||||
<td>
|
||||
{% if a.status == "DRAFT" %}
|
||||
{% if a.ad != null %} {{ a.ad.countAnswers }}{% endif %}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{% if a.status == "DRAFT" and a.ad != null %}
|
||||
<a href="{{ path('talent_answer_create',{id:a.ad.id}) }}">
|
||||
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
|
||||
</a>
|
||||
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% block content %}
|
||||
<div id="wrapper" class="contentWrapper">
|
||||
<div class="row">
|
||||
<div class="col s8 offset-s2">
|
||||
<h3>Candidature pour l'entreprise : {{ company.brandName() }}</h3>
|
||||
<div class="card-panel">
|
||||
<strong><b>Comment procéder ?</b></strong>
|
||||
<div class="row">
|
||||
<div class="col s12 m5">
|
||||
<h4 class="color-blue1">Vous ne disposez pas de CV vidéo</h4>
|
||||
<p>Vous pouvez enregistrer directement votre réponse si vous disposez d'une webcam en <b>cliquant
|
||||
sur le bouton ci-dessous</b>.
|
||||
Une fois votre vidéo enregistrée et téléchargée vous pouvez la déposer dans la zone de
|
||||
téléchargement afin de valider votre réponse.</p><br/>
|
||||
<a class="modal-trigger waves-effect waves-light btn btn-default" href="#modal1">Filmer mon
|
||||
CV vidéo</a>
|
||||
</div>
|
||||
<div class="col offset-m2 s12 m5">
|
||||
<h4 class="color-blue1">Vous disposez déjà d'un cv vidéo</h4>
|
||||
<p>Pour répondre à cette annonce il vous suffit de glisser votre vidéo de réponse dans la
|
||||
<strong><a href="#infosDropzone">zone ci-dessous</a></strong>. Une fois la vidéo
|
||||
téléchargée vous pourrez valider votre réponse. Vous pouvez néanmoins laisser
|
||||
un message en complément de la vidéo dans la zone prévue à cette effet plus bas.
|
||||
</p>
|
||||
<a class="modal-trigger waves-effect waves-light btn btn-default" href="#modal2">Choisir une
|
||||
vidéo de ma vidéothèque</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p></p>
|
||||
{% set video = null %}
|
||||
{% include 'media/dropzone.html.twig' %}
|
||||
{{ form_start(form, {'attr': { 'id': 'form_media' } }) }}
|
||||
{{ form_row(form.status) }}
|
||||
<br/><br/>
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
{{ form_errors(form.comment) }}
|
||||
{{ form_widget(form.comment) }}
|
||||
{{ form_label(form.comment) }}
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
{{ form_rest(form) }}
|
||||
<button id="submit" class="btn btn-large btn-default disabled" type="submit">
|
||||
Valider ma réponse
|
||||
</button>
|
||||
{{ form_end(form) }}
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal Vidéotheque -->
|
||||
<div id="modal2" class="modal modal-fixed-footer">
|
||||
<div class="modal-content modal-choiceExistingVideo">
|
||||
<h2>Choisir une vidéo existante</h2>
|
||||
<div class="row">
|
||||
{% for vid in videos %}
|
||||
{% set img = "" %}
|
||||
{% if vid.thumbnails.0 is defined %}
|
||||
{% set img = vid.thumbnails.0.sizes.4.link %}
|
||||
{% endif %}
|
||||
<div class="col s4">
|
||||
<a class="addExistingVideo tooltipped btn btn-default"
|
||||
data-url="https://player.vimeo.com{{ vid.getCdnUrl()|replace({ "videos": "video"}) }}"
|
||||
data-img="{{ img }}" data-position="top" data-tooltip="Choisir cette vidéo" href="#"
|
||||
data-title="{{ vid.getTitle() }}" data-id="{{ vid.getId() }}">
|
||||
<span class="truncate">{{ vid.getTitle() }}</span><br/>
|
||||
</a>
|
||||
<iframe src="https://player.vimeo.com{{ vid.getCdnUrl()|replace({ "videos": "video"}) }}"
|
||||
width="100%" height="auto" frameborder="0" webkitallowfullscreen mozallowfullscreen
|
||||
allowfullscreen></iframe>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal Structure -->
|
||||
<div id="modal1" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col m5 s12">
|
||||
<h4 class="color-blue1">J'enregistre mon CV vidéo</h4>
|
||||
<video id="myCvVideo" class="video-js vjs-default-skin"></video>
|
||||
<div class="video-informations-card">
|
||||
<p>Merci de cliquer sur l'icone <span class="vjs-icon-device-perm"></span> afin d'activer votre
|
||||
webcam.</p>
|
||||
<p>Votre webcam activée, lancez l'enregistrement en cliquant sur le bouton rond en bas à gauche
|
||||
du lecteur.</p>
|
||||
<p>Si vous n'êtes pas satisfait de votre CV vidéo après un enregistrement, vous pouvez à tout
|
||||
moment recommencer l'enregistrement.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col m2 hide-on-small-only">
|
||||
<div class="valign-wrapper"
|
||||
style="align-items:center;justify-content: center;height: 300px;width:100%;">
|
||||
<div style="font-size:40px;">
|
||||
<i class="fa fa-arrow-right center-align" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col m5 s12">
|
||||
<h4 class="color-blue1">Je prévisualise mon CV vidéo</h4>
|
||||
<video id="preview" controls="true">
|
||||
<span class="vjs-control-text">Play Video</span>
|
||||
</video>
|
||||
<div class="btnContainer center-align" style="display:none;">
|
||||
<a id="saveRecord" href="#!" class="saveVideo btn btn-default">
|
||||
<i class="fa fa-floppy-o" aria-hidden="true"></i> Télécharger mon cv-vidéo
|
||||
</a>
|
||||
<div class="video-informations-card">
|
||||
<p>Une fois téléchargé vous pouvez glisser-déposer votre CV-vidéo dans la zone prévue pour
|
||||
lancer le téléchargement.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="modal-action modal-close waves-effect waves-blue btn-flat ">Fermer</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDYV8IJGtIoWLfXJQQvIuDQ88n2aEuRgpg&libraries=places"></script>
|
||||
<script type="text/javascript">
|
||||
var urlDropzone = '{{ path("upload_media_answer", {"id": answer.getId()}) }}';
|
||||
var urlDelete = '{{ path("delete_media_answer", {"id": answer.getId() }) }}';
|
||||
var idObject = '{{ answer.getId() }}';
|
||||
var typeObject = "answer";
|
||||
var videoExist = false;
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
$(".addExistingVideo").click(function () {
|
||||
var idVideo = $(this).data("id");
|
||||
var titleVideo = $(this).data("title");
|
||||
var imgVideo = $(this).data("img");
|
||||
var urlVideo = $(this).data("url");
|
||||
var drop = $("#my-awesome-dropzone")[0].dropzone;
|
||||
drop.removeAllFiles(true);
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '{{ path("link_media_answer", {"id": answer.getId()}) }}',
|
||||
data: "idVideo=" + idVideo,
|
||||
dataType: 'html',
|
||||
success: function (response) {
|
||||
Materialize.toast('Vidéo associé Merci', 4000);
|
||||
$('#modal2').modal();
|
||||
$('#modal2').modal('close');
|
||||
videoOriginal = urlVideo;
|
||||
videoExist = true;
|
||||
var mockFile = {name: titleVideo, size: '100', type: 'mp4', serverID: 0, accepted: true};
|
||||
drop.emit("addedfile", mockFile);
|
||||
drop.files.push(mockFile);
|
||||
iframe = '{% include "media/dropzoneTemplateSuccess.html.twig" with {"url_iframe": "#URL_PLACEHOLDER#"} only %}';
|
||||
iframe = iframe.replace('#URL_PLACEHOLDER#', urlVideo);
|
||||
if ($('#preview_video > iframe').length) {
|
||||
$('#preview_video > iframe').attr('src', urlVideo);
|
||||
$('#preview_video > *').fadeIn();
|
||||
if ($("#form_media #submit").hasClass('disabled')) {
|
||||
$("#form_media > #submit").removeClass('disabled');
|
||||
$("#form_media > #submit").addClass('pulse');
|
||||
}
|
||||
} else {
|
||||
$('#preview_video').html(iframe);
|
||||
}
|
||||
if ($("#form_media #submit").hasClass('disabled')) {
|
||||
$("#form_media > #submit").removeClass('disabled');
|
||||
$("#form_media > #submit").addClass('pulse');
|
||||
}
|
||||
},
|
||||
error: function (response, xhr, ajaxOptions, thrownError) {
|
||||
Materialize.toast('Erreur lors de l\'association de la vidéo!', 4000);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="{{ asset('/js/dropzone.js') }}"></script>
|
||||
<script src="{{ asset('/js/vendors/video.min.js') }}"></script>
|
||||
<script src="{{ asset('/js/vendors/RecordRTC.js') }}"></script>
|
||||
<script src="{{ asset('/js/vendors/videojs.record.js') }}"></script>
|
||||
<script src="{{ asset('/js/vendors/jquery.browser.min.js') }}"></script>
|
||||
<script language="JavaScript">
|
||||
var player = videojs("myCvVideo",
|
||||
{
|
||||
controls: true,
|
||||
width: 320,
|
||||
height: 240,
|
||||
plugins: {
|
||||
record: {
|
||||
audio: true,
|
||||
video: true,
|
||||
maxLength: 300,
|
||||
debug: true,
|
||||
videoMimeType: 'video/mp4',
|
||||
}
|
||||
}
|
||||
});
|
||||
player.on('deviceError', function () {
|
||||
console.log('device error:', player.deviceErrorCode);
|
||||
});
|
||||
player.on('error', function (error) {
|
||||
console.log('error:', error);
|
||||
});
|
||||
player.on('startRecord', function () {
|
||||
console.log('started recording!');
|
||||
});
|
||||
player.on('finishRecord', function () {
|
||||
$(".btnContainer").fadeIn();
|
||||
var preview = document.querySelector('#preview');
|
||||
var file = player.recordedData;
|
||||
var agent = navigator.userAgent;
|
||||
if ($.browser.mozilla) {
|
||||
window.URL = window.URL || window.webkitURL;
|
||||
fileURL = window.URL.createObjectURL(file);
|
||||
preview.src = fileURL;
|
||||
} else {
|
||||
preview.src = URL.createObjectURL(file.video);
|
||||
}
|
||||
});
|
||||
$("#saveRecord").on('click', function () {
|
||||
player.recorder.saveAs({'video': 'mon-cv-talentstube'});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -3,13 +3,20 @@
|
||||
<div class="profil center-align">
|
||||
{% set company = app.user.company %}
|
||||
<div class="profil-picture">
|
||||
{% if app.user.photo %}
|
||||
{% if app.user.photo is not null %}
|
||||
<img src="{{ app.user.webPath | imagine_filter('profile_square') }}"
|
||||
alt="{{ app.user.firstname }} {{ app.user.lastname|first|upper }}"
|
||||
class="circle responsive-img"/>
|
||||
<a href="#modalProfilePicture">
|
||||
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% elseif app.user.socialPicture is not null %}
|
||||
<img src="{{ app.user.socialPicture }}"
|
||||
alt="{{ app.user.firstname }} {{ app.user.lastname|first|upper }}"
|
||||
class="circle responsive-img"/>
|
||||
<a href="#modalProfilePicture">
|
||||
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="circle-letters font-2">{{ app.user.firstname|first|upper }}{{ app.user.lastname|first|upper }}</div>
|
||||
<a href="#modalProfilePicture">
|
||||
@@ -73,11 +80,11 @@
|
||||
<div id="modalProfilePicture" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Edition de votre photo de profil</h4>
|
||||
<div class="row">
|
||||
{#<div class="row">
|
||||
{{ render(controller(
|
||||
'AppBundle:Media:profilePicture'
|
||||
)) }}
|
||||
</div>
|
||||
</div>#}
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
<div class="col s12">
|
||||
<span class="bg-blue-1 text-gradient font-2 fs-20">Mes dernières offres consultées</span>
|
||||
</div>
|
||||
<div class="owl-carousel">
|
||||
<div class="owl-carousel owl-carousel-ptalent">
|
||||
{% for historyAd in historyAds %}
|
||||
<div class="item">
|
||||
<a href="{{ path('ad_show',{'alias': historyAd.alias }) }}"
|
||||
|
||||
@@ -25,7 +25,9 @@ framework:
|
||||
engines: ['twig']
|
||||
default_locale: "%locale%"
|
||||
trusted_hosts: ~
|
||||
trusted_proxies: ~
|
||||
# http://symfony.com/doc/master/deployment/proxies.html
|
||||
# https://symfony.com/blog/fixing-the-trusted-proxies-configuration-for-symfony-3-3
|
||||
trusted_proxies: [192.0.0.1, 10.0.0.0/8, 172.16.0.0/12]
|
||||
session:
|
||||
handler_id: session.handler.native_file
|
||||
save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"
|
||||
@@ -157,6 +159,40 @@ doctrine_migrations:
|
||||
table_name: migration_versions
|
||||
name: Application Migrations
|
||||
|
||||
hwi_oauth:
|
||||
connect:
|
||||
confirmation: false
|
||||
account_connector: app.user_provider
|
||||
target_path_parameter: _destination
|
||||
#use_referer: true
|
||||
firewall_names:
|
||||
- main
|
||||
fosub:
|
||||
username_iterations: 30
|
||||
properties:
|
||||
facebook: facebookId
|
||||
linkedin: linkedinId
|
||||
resource_owners:
|
||||
facebook:
|
||||
type: facebook
|
||||
client_id: 1800752833559014
|
||||
client_secret: 454de729b1e58f506f94c8b50ca5c011
|
||||
scope: "email, public_profile, user_birthday"
|
||||
infos_url: "https://graph.facebook.com/me?fields=id,email,first_name,last_name,gender,birthday,picture.type(square)"
|
||||
paths:
|
||||
email: email
|
||||
profilepicture: picture.data.url
|
||||
options:
|
||||
display: popup
|
||||
#csrf: true
|
||||
linkedin:
|
||||
type: linkedin
|
||||
client_id: 77ywkne3b8tcnk
|
||||
client_secret: HB9Zdx42s7RPLUqm
|
||||
infos_url: "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,location,email-address,picture-urls::(original))?format=json"
|
||||
options:
|
||||
#csrf: true
|
||||
|
||||
# Monolog
|
||||
monolog:
|
||||
handlers:
|
||||
|
||||
@@ -30,5 +30,5 @@ monolog:
|
||||
# type: chromephp
|
||||
# level: info
|
||||
|
||||
#swiftmailer:
|
||||
# delivery_addresses: ['me@example.com']
|
||||
swiftmailer:
|
||||
delivery_addresses: ['%delivery_addresses%']
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# This file is auto-generated during the composer install
|
||||
parameters:
|
||||
mailer_transport: smtp
|
||||
mailer_host: mail.gandi.net
|
||||
mailer_user: noreply@talentstube.com
|
||||
mailer_password: zfYwOGBHweCQ8OI9ghF6lUyo
|
||||
secret: 416157a2389cec970df2a46fa8ffd361b76516d1
|
||||
database_host: 'tt-website.postgres.dev'
|
||||
database_host: tt-website.postgres.dev
|
||||
database_port: '5432'
|
||||
database_name: database
|
||||
database_user: user
|
||||
database_password: edcl56
|
||||
#Mongo
|
||||
mongodb_server: "mongodb://tt-website.mongo.dev:27017"
|
||||
#Prestashop
|
||||
mongodb_server: 'mongodb://tt-website.mongo.dev:27017'
|
||||
prestashop_key: 9YUQ5jPqT4Z4x5s9B25pb1yuGBtpaeBDxpWDgicPF3CuP8F2p9aK18ZS
|
||||
prestashop_path: /home/projects/prestashop1/store
|
||||
prestashop_id: 1
|
||||
@@ -19,11 +18,10 @@ parameters:
|
||||
prestashop_month_id: 2
|
||||
prestashop_ad_id: 1
|
||||
prestashop_video_corporate_id: 3
|
||||
prestashop_url: https://www.talentstube.com/store/
|
||||
#Media
|
||||
prestashop_url: 'https://www.talentstube.com/store/'
|
||||
media_video_dir: '%kernel.root_dir%/../web/uploads/videos/'
|
||||
media_video_url: 'http://pp-static.talentstube.com/videos/'
|
||||
media_photo_dir: '%kernel.root_dir%/../web/uploads/photos/'
|
||||
|
||||
elastic_host: tt-website.elasticsearch.dev
|
||||
elastic_port: 9200
|
||||
delivery_addresses: thomas@e-declic.com
|
||||
@@ -27,3 +27,4 @@ parameters:
|
||||
|
||||
elastic_host: tt-website.elasticsearch.preprod
|
||||
elastic_port: 9200
|
||||
delivery_addresses: thomas@e-declic.com
|
||||
@@ -13,7 +13,7 @@ parameters:
|
||||
mongodb_server: "mongodb://tt-website.mongo.prod:27017"
|
||||
#Prestashop
|
||||
prestashop_key: 9YUQ5jPqT4Z4x5s9B25pb1yuGBtpaeBDxpWDgicPF3CuP8F2p9aK18ZS
|
||||
prestashop_path: /home/www/website/www/store/
|
||||
prestashop_path: /home/www/website/store/
|
||||
prestashop_id: 1
|
||||
prestashop_avoid: false
|
||||
prestashop_month_id: 11
|
||||
|
||||
@@ -7,4 +7,16 @@ app:
|
||||
type: annotation
|
||||
|
||||
_liip_imagine:
|
||||
resource: "@LiipImagineBundle/Resources/config/routing.xml"
|
||||
resource: "@LiipImagineBundle/Resources/config/routing.xml"
|
||||
|
||||
hwi_oauth_security:
|
||||
resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
|
||||
prefix: /login
|
||||
|
||||
hwi_oauth_connect:
|
||||
resource: "@HWIOAuthBundle/Resources/config/routing/connect.xml"
|
||||
prefix: /login
|
||||
|
||||
hwi_oauth_redirect:
|
||||
resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml"
|
||||
prefix: /login
|
||||
@@ -3,7 +3,8 @@ security:
|
||||
in_memory:
|
||||
memory: ~
|
||||
fos_userbundle:
|
||||
id: fos_user.user_provider.username_email
|
||||
#id: fos_user.user_provider.username_email
|
||||
id: app.user_provider
|
||||
|
||||
firewalls:
|
||||
dev:
|
||||
@@ -15,14 +16,18 @@ security:
|
||||
form_login:
|
||||
provider: fos_userbundle
|
||||
csrf_token_generator: security.csrf.token_manager
|
||||
success_handler: redirect.after.login
|
||||
# logout: true
|
||||
logout:
|
||||
handlers: ['edeclic.prestashop.logout']
|
||||
anonymous: true
|
||||
oauth:
|
||||
resource_owners:
|
||||
facebook: "/login/check-facebook"
|
||||
linkedin: "/login/check-linkedin"
|
||||
login_path: "/login"
|
||||
oauth_user_provider:
|
||||
service: app.user_provider
|
||||
anonymous: true
|
||||
switch_user:
|
||||
provider: fos_userbundle
|
||||
|
||||
access_control:
|
||||
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||
@@ -32,9 +37,7 @@ security:
|
||||
- { path: ^/admin/config/, role: [ROLE_WEBMASTER,ROLE_SUPER_ADMIN] }
|
||||
- { path: ^/talent/me/, roles: ROLE_TALENT }
|
||||
- { path: ^/talent/recherche/, roles: ROLE_COMPANY }
|
||||
#- { path: ^/talent/(.+)/, roles: ROLE_COMPANY }
|
||||
- { path: ^/entreprise/me/, roles: ROLE_COMPANY }
|
||||
- { path: ^/annonce/(.+)/repondre, roles: ROLE_TALENT }
|
||||
- { path: ^/territoire/me/, roles: ROLE_TERRITORY }
|
||||
|
||||
encoders:
|
||||
|
||||
@@ -43,7 +43,7 @@ services:
|
||||
# Listeners
|
||||
user.registration_listener:
|
||||
class: AppBundle\Utils\EventListener\RegistrationListener
|
||||
arguments: ["@doctrine.orm.entity_manager","@mt.slug","@security.token_storage","@router"]
|
||||
arguments: ["@doctrine.orm.entity_manager","@mt.slug","@security.token_storage","@router","@fos_user.user_manager"]
|
||||
tags:
|
||||
- { name: kernel.event_subscriber }
|
||||
|
||||
@@ -72,3 +72,6 @@ services:
|
||||
class: AppBundle\Utils\Services\ProductManager
|
||||
arguments: ["@doctrine.orm.entity_manager","@security.token_storage", "@app.mail_manager"]
|
||||
|
||||
app.user_provider:
|
||||
class: AppBundle\Security\Core\User\FOSUBUserProvider
|
||||
arguments: ["@fos_user.user_manager","@doctrine.orm.entity_manager","@mt.slug","@service_container", { facebook: facebookId, linkedin: linkedinId}]
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"sensio/framework-extra-bundle": "^3.0.2",
|
||||
"incenteev/composer-parameter-handler": "^2.0",
|
||||
"twig/twig": "^1.0||^2.0",
|
||||
"friendsofsymfony/user-bundle": "~2.0@dev",
|
||||
"friendsofsymfony/user-bundle": "~2.0",
|
||||
"doctrine/mongodb-odm-bundle": "^3.0",
|
||||
"doctrine/mongodb-odm": "^1.0",
|
||||
"alcaeus/mongo-php-adapter": "^1.0",
|
||||
@@ -44,7 +44,13 @@
|
||||
"vimeo/vimeo-api": "^1.1",
|
||||
"pulse00/ffmpeg-bundle": "^0.6.0",
|
||||
"doctrine/doctrine-migrations-bundle": "^1.0",
|
||||
"nmure/crawler-detect-bundle": "^1.0.0"
|
||||
"nmure/crawler-detect-bundle": "^1.0.0",
|
||||
"hwi/oauth-bundle": "^0.6.0",
|
||||
"php-http/guzzle6-adapter": "^1.1",
|
||||
"php-http/httplug-bundle": "^1.8",
|
||||
"symfony/assetic-bundle": "^2.8",
|
||||
"friendsofsymfony/elastica-bundle": "4.1.*",
|
||||
"liip/imagine-bundle": "^1.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"sensio/generator-bundle": "^3.0",
|
||||
|
||||
Generated
+5145
File diff suppressed because it is too large
Load Diff
@@ -417,11 +417,12 @@ class AdController extends Controller
|
||||
}
|
||||
$mediaManager->getThumbnails($a);
|
||||
}
|
||||
|
||||
$session = new Session();
|
||||
|
||||
return $this->render('default/ad_show.html.twig', [
|
||||
'ad' => $ad,
|
||||
'companyAds' => $companyAds,
|
||||
'alreadyAnswered' => $alreadyAnswered
|
||||
'alreadyAnswered' => $alreadyAnswered,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,27 +4,284 @@ namespace AppBundle\Controller;
|
||||
|
||||
use AppBundle\Entity\Ad;
|
||||
use AppBundle\Entity\Answer;
|
||||
use AppBundle\Entity\Company;
|
||||
use AppBundle\Entity\Message;
|
||||
use AppBundle\Form\AnswerType;
|
||||
use AppBundle\Form\ContactTalentType;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher,
|
||||
Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken,
|
||||
Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||
|
||||
|
||||
class AnswerController extends Controller
|
||||
{
|
||||
/**
|
||||
* @Route("/anwser/edit/comment/{id}", name="anwser_edit_comment")
|
||||
*/
|
||||
public function anwserEditCommentAction(Request $request, Answer $answer)
|
||||
{
|
||||
if($answer) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$answer->setComment($request->get('comment'));
|
||||
$em->persist($answer);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
return new JsonResponse(array('success' => "true"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/annonce/{ad}/reponse/{answer}", name="answer_create_ad_step3")
|
||||
* @Security("answer.getUser().getId() == user.getId()")
|
||||
*/
|
||||
public function createAnswerAdStep3Action(Request $request, Ad $ad, Answer $answer)
|
||||
{
|
||||
return $this->finishAnswerAction($request, $ad, $answer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/company/{company}/reponse/{answer}", name="answer_create_company_step3")
|
||||
* @Security("answer.getUser().getId() == user.getId()")
|
||||
*/
|
||||
public function createAnswerCompanyStep3Action(Request $request, Company $company, Answer $answer)
|
||||
{
|
||||
return $this->finishAnswerAction($request, $company, $answer);
|
||||
}
|
||||
|
||||
public function finishAnswerAction($request, $object, $answer) {
|
||||
$securityContext = $this->container->get('security.authorization_checker');
|
||||
if($object instanceof Ad) {
|
||||
$route = "ad_show";
|
||||
$varReqAnswer = "ad";
|
||||
$contact = $object->getCreator();
|
||||
$title = $answer->getAd()->getTitle();
|
||||
}
|
||||
if($object instanceof Company) {
|
||||
$route = "company_show";
|
||||
$varReqAnswer = "company";
|
||||
$contact = $object->getMainUser();
|
||||
$title = $object->getBrandName();
|
||||
}
|
||||
if (!$securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
|
||||
return $this->redirectToRoute($route, array('alias' => $object->getAlias()));
|
||||
}
|
||||
if($answer) {
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$existAnswer = $em->getRepository('AppBundle:Answer')->findOneBy(array('user' => $this->getUser(), $varReqAnswer => $object));
|
||||
if (!$this->getUser()->hasRole('ROLE_TALENT')) {
|
||||
$em->remove($answer);
|
||||
$session = new Session();
|
||||
$session->getFlashBag()->add('popup_answer_not_allowed', true);
|
||||
} else {
|
||||
if($existAnswer && ($existAnswer->getId() !== $answer->getid())){
|
||||
$em->remove($answer);
|
||||
$session = new Session();
|
||||
$session->getFlashBag()->add('popup_answer_already_send', true);
|
||||
} else {
|
||||
$session = new Session();
|
||||
$session->getFlashBag()->add('popup_answer_already_send', false);
|
||||
$answer->setStatus('SENDED');
|
||||
$em->persist($answer);
|
||||
|
||||
$mediaManager = $this->get('media_manager');
|
||||
$mediaManager->getThumbnails($answer);
|
||||
$thumbnailsArray = $answer->getVideo()->getThumbnails();
|
||||
$img = $thumbnailsArray[0]['sizes'][3]['link'];
|
||||
$mailManager = $this->get('app.mail_manager');
|
||||
$link = $this->generateUrl('company_answer_show', array('id' => $answer->getId()),UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$mailManager->sendMail(
|
||||
'Candidature TalentsTube ',
|
||||
'<div style="margin:0 auto;max-width:600px;background:#FFFFFF;"><table class="" cellpadding="0" cellspacing="0" style="width:100%;font-size:0px;background:#FFFFFF;" align="center"><tbody><tr><td style="text-align:center;vertical-align:top;font-size:0;padding:0px;"><!--[if mso]>
|
||||
<table border="0" cellpadding="0" cellspacing="0"><tr><td style="width:600px;">
|
||||
<![endif]--><div style="vertical-align:top;display:inline-block;font-size:13px;text-align:left;width:100%;" class="mj-column-per-100" aria-labelledby="mj-column-per-100"><table width="100%"><tbody><tr><td style="font-size:0;padding:0px;" align="center"><table cellpadding="0" cellspacing="0" style="border-collapse:collapse;border-spacing:0px;" align="center"><tbody><tr><td><img alt="" src="'.$img.'" style="border:none;display:block;outline:none;text-decoration:none;width:100%;max-width:600px;height:auto;" width="600" height="auto"></td></tr></tbody></table></td></tr></tbody></table></div><!--[if mso]>
|
||||
</td></tr></table>
|
||||
<![endif]--></td></tr></tbody></table></div>
|
||||
|
||||
<div style="margin:0 auto;max-width:600px;background:#FFFFFF;"><table class="" cellpadding="0" cellspacing="0" style="width:100%;font-size:0px;background:#FFFFFF;" align="center"><tbody><tr><td style="text-align:center;vertical-align:top;font-size:0;padding:0px;"><!--[if mso]>
|
||||
<table border="0" cellpadding="0" cellspacing="0"><tr><td style="width:600px;">
|
||||
<![endif]--><div style="vertical-align:top;display:inline-block;font-size:13px;text-align:left;width:100%;" class="mj-column-per-100" aria-labelledby="mj-column-per-100"><table width="100%"><tbody><tr><td style="font-size:0;padding:20px 35px 20px 35px;" align="left"><div class="mj-content" style="cursor:auto;color:#534F4F;font-family:Trebuchet,Helvetica,sans-serif;font-size:14px;line-height:22px;"><p>Bonjour '.$contact->getLastname().' '.$contact->getFirstname().',</p>
|
||||
<p>Le talent '.$answer->getUser()->getFirstname().' '.$answer->getUser()->getLastname().' a répondu à votre annonce '.$title.'.</p></div></td></tr></tbody></table></div><!--[if mso]>
|
||||
</td></tr></table>
|
||||
<![endif]--></td></tr></tbody></table></div>
|
||||
|
||||
<div style="margin:0 auto;max-width:600px;background:#FFFFFF;"><table class="" cellpadding="0" cellspacing="0" style="width:100%;font-size:0px;background:#FFFFFF;" align="center"><tbody><tr><td style="text-align:center;vertical-align:top;font-size:0;padding:0px 0px 40px 0px;"><!--[if mso]>
|
||||
<table border="0" cellpadding="0" cellspacing="0"><tr><td style="width:600px;">
|
||||
<![endif]--><div style="vertical-align:top;display:inline-block;font-size:13px;text-align:left;width:100%;" class="mj-column-per-100" aria-labelledby="mj-column-per-100"><table width="100%"><tbody><tr><td style="font-size:0;padding:15px 30px;" align="center"><table cellpadding="0" cellspacing="0" style="border:none;border-radius:30px;" align="center"><tbody><tr><td style="background:#1689FB;border-radius:30px;color:#FFFFFF;cursor:auto;" align="center" valign="middle" bgcolor="#1689FB"><a class="mj-content" href="'.$link.'" style="display:inline-block;text-decoration:none;background:#1689FB;border:1px solid #1689FB;border-radius:30px;color:#FFFFFF;font-family:Trebuchet,Helvetica,sans-serif;font-size:14px;font-weight:normal;padding:15px 30px;" target="_blank">Découvrir le talent</a></td></tr></tbody></table></td></tr></tbody></table></div><!--[if mso]>
|
||||
</td></tr></table>
|
||||
<![endif]--></td></tr></tbody></table></div>',
|
||||
$contact,
|
||||
'contact_default'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$session->getFlashBag()->add('popup_answer_send', true);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
return $this->redirect($this->generateUrl($route, array('alias' => $object->getAlias())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/annonce/{id}/repondre/{type}", name="answer_create_ad_step2")
|
||||
*/
|
||||
public function createAnswerAdAction(Request $request, Ad $ad, $type = 'default')
|
||||
{
|
||||
return $this->generateStep2($request, $ad, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/entreprise/{id}/repondre/{type}", name="answer_create_company_step2")
|
||||
*/
|
||||
public function createAnswerCompanyAction(Request $request, Company $company, $type = 'default')
|
||||
{
|
||||
return $this->generateStep2($request, $company, $type);
|
||||
}
|
||||
|
||||
public function generateStep2(Request $request, $object, $type) {
|
||||
if($object instanceof Ad) {
|
||||
$route = "ad_show";
|
||||
$routeStep3 = 'answer_create_ad_step3';
|
||||
$varReqAnswer = "ad";
|
||||
$typeObject = "Ad";
|
||||
}
|
||||
if($object instanceof Company) {
|
||||
$route = "company_show";
|
||||
$routeStep3 = 'answer_create_company_step3';
|
||||
$varReqAnswer = "company";
|
||||
$typeObject = "Company";
|
||||
}
|
||||
$securityContext = $this->container->get('security.authorization_checker');
|
||||
if (!$securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
|
||||
return $this->redirectToRoute($route, array('alias' => $object->getAlias()));
|
||||
}
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$user = $this->getUser();
|
||||
$existAnswer = $em->getRepository('AppBundle:Answer')->findOneBy(array('user' => $user, $varReqAnswer => $object, 'status' => 'DRAFT'));
|
||||
if ($existAnswer) {
|
||||
$answer = $existAnswer;
|
||||
} else {
|
||||
return $this->redirectToRoute($route, array('alias' => $object->getAlias()));
|
||||
}
|
||||
$video = $answer->getVideo();
|
||||
$videos = $em->getRepository('AppBundle:Media')->findBy(array('creator' => $this->getUser()));
|
||||
$form = $this->createForm(AnswerType::class, $answer);
|
||||
$form->handleRequest($request);
|
||||
$mustLogin = false;
|
||||
if (!$user->hasRole('ROLE_TALENT')) {
|
||||
$mustLogin = true;
|
||||
}
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
if(!$mustLogin){
|
||||
return $this->redirect($this->generateUrl($routeStep3, array($varReqAnswer => $object->getId(), 'answer'=> $answer->getId())));
|
||||
}
|
||||
}
|
||||
|
||||
/*Definition des urls de destination pour ouath login et register*/
|
||||
$destination_login = $this->generateUrl($routeStep3, array($varReqAnswer => $object->getId(), 'answer'=> $answer->getId()));
|
||||
$this->get('twig')->addGlobal('destination_login', $destination_login);
|
||||
$session = new Session();
|
||||
$session->set('registration_destination', $destination_login);
|
||||
|
||||
return $this->render('answer/answer_create_'.$type.'.twig', array(
|
||||
'routeObject' => $route,
|
||||
'typeObject' => $typeObject,
|
||||
'object' => $object,
|
||||
'form' => $form->createView(),
|
||||
'answer' => $answer,
|
||||
'video' => $video,
|
||||
'videos' => $videos,
|
||||
$varReqAnswer => $object,
|
||||
'mustLogin' => $mustLogin
|
||||
));
|
||||
}
|
||||
|
||||
private function createAnonymousUser() {
|
||||
$userManager = $this->get('fos_user.user_manager');
|
||||
$user = $userManager->createUser();
|
||||
$uniqId = uniqId();
|
||||
$user->setUsername("anonym_".$uniqId);
|
||||
$user->setFirstname("anonym");
|
||||
$user->setLastname("anonym");
|
||||
$user->setEmail("anonym_".$uniqId."@talentstube.com");
|
||||
$user->setPlainPassword($uniqId);
|
||||
$user->setEnabled(false);
|
||||
$user->setRoles(array('ROLE_TALENT_TMP'));
|
||||
$company = new Company();
|
||||
$company->setAlias("anonym_".$uniqId);
|
||||
$company->setType("TALENT_TMP");
|
||||
$user->setCompany($company);
|
||||
$userManager->updateUser($user);
|
||||
$token = new UsernamePasswordToken($user, $user->getPassword(), "public", $user->getRoles());
|
||||
$this->get("security.token_storage")->setToken($token);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/entreprise/{id}/repondre/", name="talent_spontaneous_application_create")
|
||||
*/
|
||||
public function talentCreateSpontaneaousAction(Request $request, Company $company)
|
||||
{
|
||||
$securityContext = $this->container->get('security.authorization_checker');
|
||||
if (!$securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
|
||||
$this->createAnonymousUser();
|
||||
}
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$existAnswer = $em->getRepository('AppBundle:Answer')->findOneBy(array('user' => $this->getUser(), 'company' => $company));
|
||||
if ($existAnswer) {
|
||||
if ($existAnswer->getStatus() == "SENDED") {
|
||||
$answer = new Answer($this->getUser(), null, 'DRAFT', $company);
|
||||
$em->persist($answer);
|
||||
$em->flush();
|
||||
} else {
|
||||
$answer = $existAnswer;
|
||||
}
|
||||
} else {
|
||||
$answer = new Answer($this->getUser(), null, 'DRAFT', $company);
|
||||
$em->persist($answer);
|
||||
$em->flush();
|
||||
}
|
||||
$em->persist($answer);
|
||||
$em->flush();
|
||||
$user = $this->getUser();
|
||||
$mustLogin = false;
|
||||
if (!$user->hasRole('ROLE_TALENT')) {
|
||||
$mustLogin = true;
|
||||
}
|
||||
$route_videotheque = $this->generateUrl("answer_create_company_step2", array('id' => $company->getId(), 'type' => 'videotheque'));
|
||||
$this->get('twig')->addGlobal('destination_login', $route_videotheque);
|
||||
|
||||
return $this->render('answer/answer_create_step1.html.twig', array(
|
||||
'typeObject' => 'Company',
|
||||
'answer' => $answer,
|
||||
'object' => $company,
|
||||
'mustLogin' => $mustLogin,
|
||||
'route_videotheque' => $route_videotheque
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/annonce/{id}/repondre/", name="talent_answer_create")
|
||||
*/
|
||||
public function talentCreateAnswerAction(Request $request, Ad $ad)
|
||||
{
|
||||
|
||||
$securityContext = $this->container->get('security.authorization_checker');
|
||||
if (!$securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
|
||||
$this->createAnonymousUser();
|
||||
}
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$existAnswer = $em->getRepository('AppBundle:Answer')->findOneBy(array('user' => $this->getUser(), 'ad' => $ad));
|
||||
|
||||
if ($existAnswer) {
|
||||
if ($existAnswer->getStatus() == "SENDED") {
|
||||
return $this->redirectToRoute('ad_show', array('alias' => $ad->getAlias()));
|
||||
@@ -35,65 +292,37 @@ class AnswerController extends Controller
|
||||
$em->persist($answer);
|
||||
$em->flush();
|
||||
}
|
||||
$videos = $em->getRepository('AppBundle:Media')->findBy(array('creator' => $this->getUser()));
|
||||
$video = $answer->getVideo();
|
||||
$form = $this->createForm(AnswerType::class, $answer);
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$answer = $form->getData();
|
||||
$answer->setStatus('SENDED');
|
||||
$em->persist($answer);
|
||||
$em->flush();
|
||||
$link = $this->generateUrl('company_answer_show', array('id' => $answer->getAd()->getId()),UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
|
||||
$mediaManager = $this->get('media_manager');
|
||||
$mediaManager->getThumbnails($answer);
|
||||
$thumbnailsArray = $answer->getVideo()->getThumbnails();
|
||||
$img = $thumbnailsArray[0]['sizes'][3]['link'];
|
||||
$mailManager = $this->get('app.mail_manager');
|
||||
$mailManager->sendMail(
|
||||
'Candidature TalentsTube ',
|
||||
'<div style="margin:0 auto;max-width:600px;background:#FFFFFF;"><table class="" cellpadding="0" cellspacing="0" style="width:100%;font-size:0px;background:#FFFFFF;" align="center"><tbody><tr><td style="text-align:center;vertical-align:top;font-size:0;padding:0px;"><!--[if mso]>
|
||||
<table border="0" cellpadding="0" cellspacing="0"><tr><td style="width:600px;">
|
||||
<![endif]--><div style="vertical-align:top;display:inline-block;font-size:13px;text-align:left;width:100%;" class="mj-column-per-100" aria-labelledby="mj-column-per-100"><table width="100%"><tbody><tr><td style="font-size:0;padding:0px;" align="center"><table cellpadding="0" cellspacing="0" style="border-collapse:collapse;border-spacing:0px;" align="center"><tbody><tr><td><img alt="" src="'.$img.'" style="border:none;display:block;outline:none;text-decoration:none;width:100%;max-width:600px;height:auto;" width="600" height="auto"></td></tr></tbody></table></td></tr></tbody></table></div><!--[if mso]>
|
||||
</td></tr></table>
|
||||
<![endif]--></td></tr></tbody></table></div>
|
||||
|
||||
<div style="margin:0 auto;max-width:600px;background:#FFFFFF;"><table class="" cellpadding="0" cellspacing="0" style="width:100%;font-size:0px;background:#FFFFFF;" align="center"><tbody><tr><td style="text-align:center;vertical-align:top;font-size:0;padding:0px;"><!--[if mso]>
|
||||
<table border="0" cellpadding="0" cellspacing="0"><tr><td style="width:600px;">
|
||||
<![endif]--><div style="vertical-align:top;display:inline-block;font-size:13px;text-align:left;width:100%;" class="mj-column-per-100" aria-labelledby="mj-column-per-100"><table width="100%"><tbody><tr><td style="font-size:0;padding:20px 35px 20px 35px;" align="left"><div class="mj-content" style="cursor:auto;color:#534F4F;font-family:Trebuchet,Helvetica,sans-serif;font-size:14px;line-height:22px;"><p>Bonjour '.$ad->getCreator()->getLastname().' '.$ad->getCreator()->getFirstname().',</p>
|
||||
<p>Le talent '.$answer->getUser()->getFirstname().' '.$answer->getUser()->getLastname().' a répondu à votre annonce '.$answer->getAd()->getTitle().'.</p></div></td></tr></tbody></table></div><!--[if mso]>
|
||||
</td></tr></table>
|
||||
<![endif]--></td></tr></tbody></table></div>
|
||||
|
||||
<div style="margin:0 auto;max-width:600px;background:#FFFFFF;"><table class="" cellpadding="0" cellspacing="0" style="width:100%;font-size:0px;background:#FFFFFF;" align="center"><tbody><tr><td style="text-align:center;vertical-align:top;font-size:0;padding:0px 0px 40px 0px;"><!--[if mso]>
|
||||
<table border="0" cellpadding="0" cellspacing="0"><tr><td style="width:600px;">
|
||||
<![endif]--><div style="vertical-align:top;display:inline-block;font-size:13px;text-align:left;width:100%;" class="mj-column-per-100" aria-labelledby="mj-column-per-100"><table width="100%"><tbody><tr><td style="font-size:0;padding:15px 30px;" align="center"><table cellpadding="0" cellspacing="0" style="border:none;border-radius:30px;" align="center"><tbody><tr><td style="background:#1689FB;border-radius:30px;color:#FFFFFF;cursor:auto;" align="center" valign="middle" bgcolor="#1689FB"><a class="mj-content" href="'.$link.'" style="display:inline-block;text-decoration:none;background:#1689FB;border:1px solid #1689FB;border-radius:30px;color:#FFFFFF;font-family:Trebuchet,Helvetica,sans-serif;font-size:14px;font-weight:normal;padding:15px 30px;" target="_blank">Découvrir le talent</a></td></tr></tbody></table></td></tr></tbody></table></div><!--[if mso]>
|
||||
</td></tr></table>
|
||||
<![endif]--></td></tr></tbody></table></div>',
|
||||
$ad->getCreator(),
|
||||
'contact_default'
|
||||
);
|
||||
|
||||
$this->addFlash('notice', $this->get('translator')->trans('ad.answer.mail.send'));
|
||||
return $this->redirectToRoute('ad_show', array('alias' => $ad->getAlias()));
|
||||
$user = $this->getUser();
|
||||
$mustLogin = false;
|
||||
if (!$user->hasRole('ROLE_TALENT')) {
|
||||
$mustLogin = true;
|
||||
}
|
||||
|
||||
return $this->render('talent/answer_create.html.twig', array(
|
||||
'form' => $form->createView(),
|
||||
$route_videotheque = $this->generateUrl("answer_create_ad_step2", array('id' => $ad->getId(), 'type' => 'videotheque'));
|
||||
$this->get('twig')->addGlobal('destination_login', $route_videotheque);
|
||||
|
||||
return $this->render('answer/answer_create_step1.html.twig', array(
|
||||
'typeObject' => 'Ad',
|
||||
'answer' => $answer,
|
||||
'video' => $video,
|
||||
'videos' => $videos,
|
||||
'ad' => $ad
|
||||
'object' => $ad,
|
||||
'mustLogin' => $mustLogin,
|
||||
'route_videotheque' => $route_videotheque
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/entreprise/me/espace-oe/reponse/{id}/", name="company_answer_show")
|
||||
* @Security("answer.getAd().getCompany() == user.getCompany()")
|
||||
* @Security("answer.checkCompany(user.getCompany()) == true")
|
||||
*/
|
||||
public function companyShowAnswerAction(Request $request, Answer $answer)
|
||||
{
|
||||
if ($answer->getAd() == null) {
|
||||
$type = 'spontaneous';
|
||||
$title = $answer->getCompany()->getBrandName();
|
||||
} else {
|
||||
$type = 'answer';
|
||||
$title = $answer->getAd()->getTitle();
|
||||
}
|
||||
$user = $this->getUser();
|
||||
$form = $this->createForm(ContactTalentType::class);
|
||||
$form->handleRequest($request);
|
||||
@@ -109,15 +338,15 @@ class AnswerController extends Controller
|
||||
'user' => $answer->getUser(),
|
||||
));
|
||||
$email = \Swift_Message::newInstance()
|
||||
->setSubject('Candidature : ' . $answer->getAd()->getTitle())
|
||||
->setFrom($this->getParameter('mailer_user'),$user->getEmail())
|
||||
->setSubject('Candidature : ' . $title)
|
||||
->setFrom($this->getParameter('mailer_user'), $user->getEmail())
|
||||
->setReplyTo($user->getEmail())
|
||||
->setTo($answer->getUser()->getEmail())
|
||||
->setBody($htmlView, 'text/html')
|
||||
->addPart($plainView, 'text/plain');
|
||||
$this->get('mailer')->send($email);
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$messageObj = new message('Candidature : ' . $answer->getAd()->getTitle(), $message, $user, $answer->getUser(), $form['status']->getData(), $answer);
|
||||
$messageObj = new message('Candidature : ' . $title, $message, $user, $answer->getUser(), $form['status']->getData(), $answer);
|
||||
$em->persist($messageObj);
|
||||
$em->flush();
|
||||
return $this->redirectToRoute('company_answer_show', ['id' => $answer->getId()]);
|
||||
@@ -125,6 +354,7 @@ class AnswerController extends Controller
|
||||
return $this->render('company/answer_show.html.twig', [
|
||||
'answer' => $answer,
|
||||
'form' => $form->createView(),
|
||||
'type' => $type,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -137,7 +367,23 @@ class AnswerController extends Controller
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$answers = $em->getRepository('AppBundle:Answer')->getAnswerByCompany($user->getCompany()->getId());
|
||||
return $this->render('company/answer_list.html.twig', [
|
||||
'answers' => $answers
|
||||
'answers' => $answers,
|
||||
'type' => 'answer'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/entreprise/me/espace-oe/candidature-spontanee/", name="company_spontaneous_list")
|
||||
*/
|
||||
public function companyListSpontaneousAction()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$answers = $em->getRepository('AppBundle:Answer')->findBy(array('company' => $user->getCompany()->getId(), 'status' => 'SENDED'));
|
||||
return $this->render('company/answer_list.html.twig', [
|
||||
'answers' => $answers,
|
||||
'type' => 'spontaneous'
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -187,7 +433,7 @@ class AnswerController extends Controller
|
||||
return new JsonResponse($message);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @Route("/media/linkAnswer/{id}", name="link_media_answer")
|
||||
*/
|
||||
public function mediaLinkAction(Request $request, Answer $answer)
|
||||
|
||||
@@ -7,6 +7,7 @@ use AppBundle\Entity\Company;
|
||||
use AppBundle\Form\AdSearchType;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
|
||||
class DefaultController extends Controller
|
||||
@@ -66,6 +67,27 @@ class DefaultController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/redirect_dashboard", name="redirect_dashboard")
|
||||
*/
|
||||
public function redirect_dashboard()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
if($user){
|
||||
if ($user->hasRole('ROLE_TALENT')) {
|
||||
|
||||
return $this->redirectToRoute('talent_dashboard');
|
||||
} elseif ($user->hasRole('ROLE_COMPANY')) {
|
||||
|
||||
return $this->redirectToRoute('company_dashboard');
|
||||
}
|
||||
} else {
|
||||
|
||||
return $this->redirectToRoute('homepage');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/test", name="test")
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,7 @@ class NoteController extends Controller
|
||||
|
||||
/**
|
||||
* @Route("/entreprise/me/espace-oe/reponse/{id}/note/create", name="answer_note_create")
|
||||
* @Security("answer.getAd().getCompany() == user.getCompany()")
|
||||
* @Security("answer.checkCompany(user.getCompany()) == true")
|
||||
*/
|
||||
public function CompanyCreateAnswerNoteAction(Request $request, Answer $answer)
|
||||
{
|
||||
|
||||
@@ -35,11 +35,15 @@ class SecurityModalController extends Controller
|
||||
$csrfToken = $this->has('security.csrf.token_manager')
|
||||
? $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue()
|
||||
: null;
|
||||
|
||||
$formFactory = $this->get('fos_user.registration.form.factory');
|
||||
$form = $formFactory->createForm();
|
||||
|
||||
return $this->render('@FOSUser/Security/loginModal.html.twig', array(
|
||||
'last_username' => $lastUsername,
|
||||
'error' => $error,
|
||||
'csrf_token' => $csrfToken,
|
||||
'register_form' => $form->createView()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -392,9 +392,9 @@ class Ad
|
||||
public function getHtmlDescription()
|
||||
{
|
||||
$address = $this->getAddress();
|
||||
$desc = 'Découvrez la vidéo de l\'offre d\'emploi ' . $this->title . ' - ';
|
||||
$desc = 'Découvrez la vidéo de l\'offre d\'emploi ' . $this->title;
|
||||
if (isset($address['locality'])) {
|
||||
$desc .= $address['locality'];
|
||||
$desc .= ' - ' .$address['locality'];
|
||||
}
|
||||
if (isset($address['postal_code'])) {
|
||||
$zip = substr($address['postal_code'], 0, 2);
|
||||
|
||||
@@ -32,9 +32,16 @@ class Answer
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Ad", inversedBy="answers")
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
*/
|
||||
private $ad;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Company", inversedBy="spontaneousApplications")
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
*/
|
||||
private $company;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="answers", cascade={"persist"})
|
||||
*/
|
||||
@@ -60,8 +67,8 @@ class Answer
|
||||
private $comment;
|
||||
|
||||
/***********************
|
||||
* OneToMany Array's
|
||||
***********************/
|
||||
* OneToMany Array's
|
||||
***********************/
|
||||
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Note", cascade={"persist"})
|
||||
@@ -76,17 +83,40 @@ class Answer
|
||||
/***********************
|
||||
* Constructor
|
||||
***********************/
|
||||
|
||||
|
||||
public function __construct($user = null, $ad = null, $status = null)
|
||||
|
||||
public function __construct($user = null, $ad = null, $status = null, $company = null)
|
||||
{
|
||||
$this->notes = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
$this->messages = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
$this->user = $user ;
|
||||
$this->ad = $ad ;
|
||||
$this->user = $user;
|
||||
$this->ad = $ad;
|
||||
$this->status = $status;
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
/***********************
|
||||
* Custom Functions
|
||||
***********************/
|
||||
|
||||
public function checkCompany(Company $company)
|
||||
{
|
||||
if ($this->ad != null) {
|
||||
$c = $this->ad->getCompany();
|
||||
} elseif ($this->company != null) {
|
||||
$c = $this->company;
|
||||
} else {
|
||||
$c = null;
|
||||
}
|
||||
if ($company == $c) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/***********************
|
||||
* Getters & Setters
|
||||
***********************/
|
||||
@@ -293,4 +323,28 @@ class Answer
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set company.
|
||||
*
|
||||
* @param \AppBundle\Entity\Company|null $company
|
||||
*
|
||||
* @return Answer
|
||||
*/
|
||||
public function setCompany(\AppBundle\Entity\Company $company = null)
|
||||
{
|
||||
$this->company = $company;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get company.
|
||||
*
|
||||
* @return \AppBundle\Entity\Company|null
|
||||
*/
|
||||
public function getCompany()
|
||||
{
|
||||
return $this->company;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,11 @@ class Company
|
||||
*/
|
||||
private $territories;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="AppBundle\Entity\Answer",mappedBy="company", cascade={"persist"})
|
||||
*/
|
||||
private $spontaneousApplications;
|
||||
|
||||
|
||||
/***********************
|
||||
* Constructor
|
||||
@@ -733,4 +738,40 @@ class Company
|
||||
{
|
||||
return $this->territories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add spontaneousApplication.
|
||||
*
|
||||
* @param \AppBundle\Entity\Answer $spontaneousApplication
|
||||
*
|
||||
* @return Company
|
||||
*/
|
||||
public function addSpontaneousApplication(\AppBundle\Entity\Answer $spontaneousApplication)
|
||||
{
|
||||
$this->spontaneousApplications[] = $spontaneousApplication;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove spontaneousApplication.
|
||||
*
|
||||
* @param \AppBundle\Entity\Answer $spontaneousApplication
|
||||
*
|
||||
* @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
|
||||
*/
|
||||
public function removeSpontaneousApplication(\AppBundle\Entity\Answer $spontaneousApplication)
|
||||
{
|
||||
return $this->spontaneousApplications->removeElement($spontaneousApplication);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get spontaneousApplications.
|
||||
*
|
||||
* @return \Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
public function getSpontaneousApplications()
|
||||
{
|
||||
return $this->spontaneousApplications;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,43 @@ class User extends BaseUser
|
||||
*/
|
||||
private $receivedMessages;
|
||||
|
||||
/************************
|
||||
* Social Network
|
||||
************************/
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="facebook_id", type="string", length=255, nullable=true)
|
||||
*/
|
||||
private $facebookId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="facebook_access_token", type="text", nullable=true)
|
||||
*/
|
||||
private $facebookAccessToken;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="linkedin_id", type="string", length=255, nullable=true)
|
||||
*/
|
||||
private $linkedinId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="linkedin_access_token", type="text", nullable=true)
|
||||
*/
|
||||
|
||||
private $linkedinAccessToken;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @ORM\Column(name="social_picture", type="text", nullable=true)
|
||||
*/
|
||||
private $socialPicture;
|
||||
|
||||
|
||||
|
||||
/***********************
|
||||
* Constructor
|
||||
@@ -123,6 +160,7 @@ class User extends BaseUser
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
parent::setUsername(uniqId());
|
||||
$this->answers = new ArrayCollection();
|
||||
$this->ads = new ArrayCollection();
|
||||
$this->bookmarks = new ArrayCollection();
|
||||
@@ -218,10 +256,20 @@ class User extends BaseUser
|
||||
}
|
||||
}
|
||||
|
||||
public function setEmail($email)
|
||||
{
|
||||
$email = is_null($email) ? '' : $email;
|
||||
parent::setEmail($email);
|
||||
$this->setUsername($email);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/***********************
|
||||
* Getters & Setters
|
||||
***********************/
|
||||
|
||||
|
||||
/**
|
||||
* Set firstname.
|
||||
*
|
||||
@@ -366,6 +414,30 @@ class User extends BaseUser
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set facebookId.
|
||||
*
|
||||
* @param string|null $facebookId
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setFacebookId($facebookId = null)
|
||||
{
|
||||
$this->facebookId = $facebookId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get facebookId.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFacebookId()
|
||||
{
|
||||
return $this->facebookId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set company.
|
||||
*
|
||||
@@ -606,22 +678,6 @@ class User extends BaseUser
|
||||
return $this->sharedBookmarks;
|
||||
}
|
||||
|
||||
public function getFile()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file
|
||||
*
|
||||
* @param UploadedFile $file
|
||||
*
|
||||
*/
|
||||
public function setFile(UploadedFile $file = null)
|
||||
{
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add sendedMessage.
|
||||
*
|
||||
@@ -693,4 +749,100 @@ class User extends BaseUser
|
||||
{
|
||||
return $this->receivedMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set linkedinId.
|
||||
*
|
||||
* @param string|null $linkedinId
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setLinkedinId($linkedinId = null)
|
||||
{
|
||||
$this->linkedinId = $linkedinId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get linkedinId.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLinkedinId()
|
||||
{
|
||||
return $this->linkedinId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set socialPicture.
|
||||
*
|
||||
* @param string|null $socialPicture
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setSocialPicture($socialPicture = null)
|
||||
{
|
||||
$this->socialPicture = $socialPicture;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get socialPicture.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSocialPicture()
|
||||
{
|
||||
return $this->socialPicture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set facebookAccessToken.
|
||||
*
|
||||
* @param string|null $facebookAccessToken
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setFacebookAccessToken($facebookAccessToken = null)
|
||||
{
|
||||
$this->facebookAccessToken = $facebookAccessToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get facebookAccessToken.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFacebookAccessToken()
|
||||
{
|
||||
return $this->facebookAccessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set linkedinAccessToken.
|
||||
*
|
||||
* @param string|null $linkedinAccessToken
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function setLinkedinAccessToken($linkedinAccessToken = null)
|
||||
{
|
||||
$this->linkedinAccessToken = $linkedinAccessToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get linkedinAccessToken.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLinkedinAccessToken()
|
||||
{
|
||||
return $this->linkedinAccessToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
namespace AppBundle\Security\Core\User;
|
||||
|
||||
|
||||
use FOS\UserBundle\Model\UserInterface as FOSUserInterface;
|
||||
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
|
||||
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseFOSUBProvider;
|
||||
use AppBundle\Entity\Company;
|
||||
use AppBundle\Entity\Answer;
|
||||
use AppBundle\Entity\Media;
|
||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||
|
||||
class FOSUBUserProvider extends BaseFOSUBProvider
|
||||
{
|
||||
protected $container;
|
||||
|
||||
public function __construct($userManager, $em, $slug, $container, array $properties)
|
||||
{
|
||||
parent::__construct($userManager,$properties);
|
||||
$this->em = $em;
|
||||
$this->slugService = $slug;
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function connect(UserInterface $user, UserResponseInterface $response)
|
||||
{
|
||||
$isTmpUser = false;
|
||||
if($this->container->get('security.token_storage')->getToken()){
|
||||
$userConnected = $this->container->get('security.token_storage')->getToken()->getUser();
|
||||
if ($userConnected->hasRole('ROLE_TALENT_TMP')) {
|
||||
$isTmpUser = true;
|
||||
$idTmpUser = $userConnected->getId();
|
||||
}
|
||||
}
|
||||
$this->disconnect($user, $response);
|
||||
$property = $this->getProperty($response);
|
||||
$username = $response->getUsername(); // get the unique user identifier
|
||||
$serviceName = $response->getResourceOwner()->getName();
|
||||
$propertyQuery = str_replace('_', '', lcfirst(ucwords($property, '_')));
|
||||
$setter = 'set' . ucfirst($serviceName) . 'Id';
|
||||
$setterToken = 'set' . ucfirst($serviceName) . 'AccessToken';
|
||||
$existingUser = $this->userManager->findUserBy(array($propertyQuery => $username));
|
||||
if (null === $existingUser) {
|
||||
$userEmail = $response->getEmail();
|
||||
$existingUser = $this->userManager->findUserByEmail($userEmail);
|
||||
}
|
||||
if (null !== $existingUser) {
|
||||
$this->disconnect($existingUser, $response);
|
||||
if($isTmpUser){
|
||||
$this->assignToRealAccount($userConnected, $existingUser);
|
||||
$user = $existingUser;
|
||||
$token = new UsernamePasswordToken($user, null, "main", $user->getRoles());
|
||||
$this->container->get("security.token_storage")->setToken($token);
|
||||
}
|
||||
$existingUser->$setter($username);
|
||||
$existingUser->$setterToken($response->getAccessToken());
|
||||
$this->userManager->updateUser($existingUser);
|
||||
} else {
|
||||
if($isTmpUser){
|
||||
$existingUser = $this->loadUserByOAuthUserResponse($response);
|
||||
$this->assignToRealAccount($userConnected, $existingUser);
|
||||
$existingUser->$setter($username);
|
||||
$existingUser->$setterToken($response->getAccessToken());
|
||||
$this->userManager->updateUser($existingUser);
|
||||
|
||||
} else {
|
||||
$user->$setter($username);
|
||||
$user->$setterToken($response->getAccessToken());
|
||||
$this->userManager->updateUser($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function assignToRealAccount($userConnected, $existingUser) {
|
||||
$answers = $this->em->getRepository('AppBundle:Answer')->findBy(array('user' => $userConnected));
|
||||
$medias = $this->em->getRepository('AppBundle:Media')->findBy(array('creator'=> $userConnected));
|
||||
foreach ($medias as $key => $media) {
|
||||
$media->setCreator($existingUser);
|
||||
}
|
||||
$this->em->flush();
|
||||
foreach ($answers as $key => $answer) {
|
||||
$answer->setUser($existingUser);
|
||||
}
|
||||
$this->em->flush();
|
||||
$this->userManager->deleteUser($userConnected);
|
||||
}
|
||||
|
||||
public function loadUserByUsername($username)
|
||||
{
|
||||
$existingUser = $this->userManager->findUserByEmail($username);
|
||||
if($this->container->get('security.token_storage')->getToken()){
|
||||
$userConnected = $this->container->get('security.token_storage')->getToken()->getUser();
|
||||
if ($userConnected->hasRole('ROLE_TALENT_TMP')) {
|
||||
|
||||
$this->assignToRealAccount($userConnected, $existingUser);
|
||||
}
|
||||
}
|
||||
|
||||
return $existingUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
|
||||
{
|
||||
$serviceName = $response->getResourceOwner()->getName();
|
||||
$data = $response->getData();
|
||||
$userConnected = null;
|
||||
if($this->container->get('security.token_storage')->getToken()){
|
||||
$userConnected = $this->container->get('security.token_storage')->getToken()->getUser();
|
||||
|
||||
}
|
||||
$userEmail = $response->getEmail();
|
||||
$user = $this->userManager->findUserByEmail($userEmail);
|
||||
|
||||
if($serviceName == "linkedin"){
|
||||
$id = $data['id'];
|
||||
$birthday = null;
|
||||
$gender = null;
|
||||
$userPhoto = $data['pictureUrls']['values'][0];
|
||||
$username = $data['firstName']."-".$data['lastName'];
|
||||
$firstname = $data['firstName'];
|
||||
$lastname = $data['lastName'];
|
||||
|
||||
} else {
|
||||
$userPhoto = 'http://graph.facebook.com/'.$data['id'].'/picture?height=200';
|
||||
$birthday = \DateTime::createFromFormat ('m/d/Y', $data['birthday']);
|
||||
$gender = $data['gender'];
|
||||
$id = $data['id'];
|
||||
$firstname = $data['first_name'];
|
||||
$lastname = $data['last_name'];
|
||||
}
|
||||
|
||||
if (null === $user) {
|
||||
$user = $this->userManager->createUser();
|
||||
$user->setUsername($firstname."-".$lastname);
|
||||
$user->setFirstname($firstname);
|
||||
$user->setLastname($lastname);
|
||||
$user->setEmail($userEmail);
|
||||
$user->setPlainPassword(uniqid());
|
||||
$user->setEnabled(true);
|
||||
$user->setSocialPicture($userPhoto);
|
||||
$user->setRoles(array('ROLE_TALENT'));
|
||||
$user->setBirthday($birthday);
|
||||
$user->setGender($gender);
|
||||
if($serviceName == "linkedin"){
|
||||
$user->setLinkedinId($id);
|
||||
}
|
||||
if($serviceName == "facebook"){
|
||||
$user->setFacebookId($id);
|
||||
}
|
||||
$this->userManager->updateUser($user);
|
||||
if ($user->getCompany() == null) {
|
||||
$company = new Company();
|
||||
$slugService = $this->slugService;
|
||||
$alias = $slugService->slugify($user->getId() . "-" . $user->getFirstname() . "-" . $user->getLastname());
|
||||
$company->setAlias($alias);
|
||||
$company->setType("TALENT");
|
||||
$user->setCompany($company);
|
||||
$this->userManager->updateUser($user);
|
||||
}
|
||||
|
||||
$token = new UsernamePasswordToken($user, null, "main", $user->getRoles());
|
||||
$this->container->get("security.token_storage")->setToken($token);
|
||||
|
||||
return $user;
|
||||
} else {
|
||||
if($userConnected) {
|
||||
$userConnected->hasRole('ROLE_TALENT_TMP');
|
||||
if ($userConnected->hasRole('ROLE_TALENT_TMP')) {
|
||||
$user->setUsername($firstname."-".$lastname);
|
||||
$user->setFirstname($firstname);
|
||||
$user->setLastname($lastname);
|
||||
$user->setEmail($userEmail);
|
||||
$user->setPlainPassword(uniqid());
|
||||
$user->setEnabled(true);
|
||||
$user->setSocialPicture($userPhoto);
|
||||
$user->setRoles(array('ROLE_TALENT'));
|
||||
$user->setBirthday($birthday);
|
||||
$user->setGender($gender);
|
||||
if($serviceName == "linkedin"){
|
||||
$user->setLinkedinId($id);
|
||||
}
|
||||
if($serviceName == "facebook"){
|
||||
$user->setFacebookId($id);
|
||||
}
|
||||
$token = new UsernamePasswordToken($user, null, "main", $user->getRoles());
|
||||
$this->container->get("security.token_storage")->setToken($token);
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$serviceName = $response->getResourceOwner()->getName();
|
||||
$setter = 'set' . ucfirst($serviceName) . 'AccessToken';
|
||||
$user->$setter($response->getAccessToken());
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ use FOS\UserBundle\Event\FilterUserResponseEvent;
|
||||
use FOS\UserBundle\Event\FormEvent;
|
||||
use FOS\UserBundle\FOSUserEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
class RegistrationListener implements EventSubscriberInterface
|
||||
{
|
||||
@@ -16,12 +18,13 @@ class RegistrationListener implements EventSubscriberInterface
|
||||
protected $security;
|
||||
protected $router;
|
||||
|
||||
public function __construct($em, $slugService, $security, $router)
|
||||
public function __construct($em, $slugService, $security, $router, $userManager)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->slugService = $slugService;
|
||||
$this->security = $security;
|
||||
$this->router = $router;
|
||||
$this->userManager = $userManager;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
@@ -48,6 +51,29 @@ class RegistrationListener implements EventSubscriberInterface
|
||||
$user->addRole('ROLE_COMPANY');
|
||||
break;
|
||||
}
|
||||
$session = new Session();
|
||||
if ($session->get('registration_destination')) {
|
||||
$userConnected = $this->security->getToken()->getUser();
|
||||
$this->em->persist($user);
|
||||
$this->assignToRealAccount($userConnected, $user);
|
||||
$redirection = $session->get('registration_destination');
|
||||
$response = new RedirectResponse($redirection);
|
||||
$event->setResponse($response);
|
||||
}
|
||||
}
|
||||
|
||||
public function assignToRealAccount($userConnected, $existingUser) {
|
||||
$answers = $this->em->getRepository('AppBundle:Answer')->findBy(array('user' => $userConnected));
|
||||
$medias = $this->em->getRepository('AppBundle:Media')->findBy(array('creator'=> $userConnected));
|
||||
foreach ($medias as $key => $media) {
|
||||
$media->setCreator($existingUser);
|
||||
}
|
||||
$this->em->flush();
|
||||
foreach ($answers as $key => $answer) {
|
||||
$answer->setUser($existingUser);
|
||||
}
|
||||
$this->em->flush();
|
||||
$this->userManager->deleteUser($userConnected);
|
||||
}
|
||||
|
||||
public function redirectUser(FilterUserResponseEvent $event)
|
||||
|
||||
@@ -84,7 +84,9 @@ class MediaManager
|
||||
{
|
||||
$video = $entity;
|
||||
if (unlink($this->mediaVideoDir . $video->getOriginalUrl())) {
|
||||
$response = $this->vimeo->request($video->getCdnUrl(), [], 'DELETE');
|
||||
if ( $this->env != "dev" ) {
|
||||
$response = $this->vimeo->request($video->getCdnUrl(), [], 'DELETE');
|
||||
}
|
||||
$this->em->remove($video);
|
||||
$this->em->flush();
|
||||
|
||||
@@ -155,7 +157,11 @@ class MediaManager
|
||||
$this->em->persist($media);
|
||||
if (!is_null($entity)) {
|
||||
if ($entity instanceof Answer) {
|
||||
$media->setTitle("Video réponse à : ".$entity->getAd()->getTitle());
|
||||
if ($entity->getAd() == null){
|
||||
$media->setTitle("Candidature pour : ".$entity->getCompany()->getBrandName());
|
||||
} else {
|
||||
$media->setTitle("Video réponse à : ".$entity->getAd()->getTitle());
|
||||
}
|
||||
$entity->setVideo($media);
|
||||
$this->em->persist($entity);
|
||||
} elseif ($entity instanceof Ad) {
|
||||
|
||||
@@ -633,12 +633,6 @@ class SymfonyRequirements extends RequirementCollection
|
||||
'Install and enable the <strong>mbstring</strong> extension.'
|
||||
);
|
||||
|
||||
$this->addRecommendation(
|
||||
function_exists('iconv'),
|
||||
'iconv() should be available',
|
||||
'Install and enable the <strong>iconv</strong> extension.'
|
||||
);
|
||||
|
||||
$this->addRecommendation(
|
||||
function_exists('utf8_decode'),
|
||||
'utf8_decode() should be available',
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
/vendor
|
||||
/composer.lock
|
||||
@@ -0,0 +1,26 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
# - 5.3 # requires old distro, see below
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
- hhvm # ignore errors, see below
|
||||
|
||||
# lock distro so new future defaults will not break the build
|
||||
dist: trusty
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.3
|
||||
dist: precise
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
|
||||
install:
|
||||
- composer install --no-interaction
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-text
|
||||
@@ -0,0 +1,54 @@
|
||||
# Changelog
|
||||
|
||||
## 1.4.0 (2017-08-18)
|
||||
|
||||
* Feature / Fix: The `fun()` function does not pass filter parameter `null`
|
||||
to underlying `stream_filter_append()` by default
|
||||
(#15 by @Nyholm)
|
||||
|
||||
Certain filters (such as `convert.quoted-printable-encode`) do not accept
|
||||
a filter parameter at all. If no explicit filter parameter is given, we no
|
||||
longer pass a default `null` value.
|
||||
|
||||
```php
|
||||
$encode = Filter\fun('convert.quoted-printable-encode');
|
||||
assert('t=C3=A4st' === $encode('täst'));
|
||||
```
|
||||
|
||||
* Add examples and improve documentation
|
||||
(#13 and #20 by @clue and #18 by @Nyholm)
|
||||
|
||||
* Improve test suite by adding PHPUnit to require-dev,
|
||||
fix HHVM build for now again and ignore future HHVM build errors,
|
||||
lock Travis distro so new future defaults will not break the build
|
||||
and test on PHP 7.1
|
||||
(#12, #14 and #19 by @clue and #16 by @Nyholm)
|
||||
|
||||
## 1.3.0 (2015-11-08)
|
||||
|
||||
* Feature: Support accessing built-in filters as callbacks
|
||||
(#5 by @clue)
|
||||
|
||||
```php
|
||||
$fun = Filter\fun('zlib.deflate');
|
||||
|
||||
$ret = $fun('hello') . $fun('world') . $fun();
|
||||
assert('helloworld' === gzinflate($ret));
|
||||
```
|
||||
|
||||
## 1.2.0 (2015-10-23)
|
||||
|
||||
* Feature: Invoke close event when closing filter (flush buffer)
|
||||
(#9 by @clue)
|
||||
|
||||
## 1.1.0 (2015-10-22)
|
||||
|
||||
* Feature: Abort filter operation when catching an Exception
|
||||
(#10 by @clue)
|
||||
|
||||
* Feature: Additional safeguards to prevent filter state corruption
|
||||
(#7 by @clue)
|
||||
|
||||
## 1.0.0 (2015-10-18)
|
||||
|
||||
* First tagged release
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Christian Lück
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,297 @@
|
||||
# clue/stream-filter [](https://travis-ci.org/clue/php-stream-filter)
|
||||
|
||||
A simple and modern approach to stream filtering in PHP
|
||||
|
||||
**Table of contents**
|
||||
|
||||
* [Why?](#why)
|
||||
* [Usage](#usage)
|
||||
* [append()](#append)
|
||||
* [prepend()](#prepend)
|
||||
* [fun()](#fun)
|
||||
* [remove()](#remove)
|
||||
* [Install](#install)
|
||||
* [Tests](#tests)
|
||||
* [License](#license)
|
||||
|
||||
## Why?
|
||||
|
||||
PHP's stream filtering system is great!
|
||||
|
||||
It offers very powerful stream filtering options and comes with a useful set of built-in filters.
|
||||
These filters can be used to easily and efficiently perform various transformations on-the-fly, such as:
|
||||
|
||||
* read from a gzip'ed input file,
|
||||
* transcode from ISO-8859-1 (Latin1) to UTF-8,
|
||||
* write to a bzip output file
|
||||
* and much more.
|
||||
|
||||
But let's face it:
|
||||
Its API is [*difficult to work with*](http://php.net/manual/en/php-user-filter.filter.php)
|
||||
and its documentation is [*subpar*](http://stackoverflow.com/questions/27103269/what-is-a-bucket-brigade).
|
||||
This combined means its powerful features are often neglected.
|
||||
|
||||
This project aims to make these features more accessible to a broader audience.
|
||||
* **Lightweight, SOLID design** -
|
||||
Provides a thin abstraction that is [*just good enough*](http://en.wikipedia.org/wiki/Principle_of_good_enough)
|
||||
and does not get in your way.
|
||||
Custom filters require trivial effort.
|
||||
* **Good test coverage** -
|
||||
Comes with an automated tests suite and is regularly tested in the *real world*
|
||||
|
||||
## Usage
|
||||
|
||||
This lightweight library consists only of a few simple functions.
|
||||
All functions reside under the `Clue\StreamFilter` namespace.
|
||||
|
||||
The below examples assume you use an import statement similar to this:
|
||||
|
||||
```php
|
||||
use Clue\StreamFilter as Filter;
|
||||
|
||||
Filter\append(…);
|
||||
```
|
||||
|
||||
Alternatively, you can also refer to them with their fully-qualified name:
|
||||
|
||||
```php
|
||||
\Clue\StreamFilter\append(…);
|
||||
```
|
||||
|
||||
### append()
|
||||
|
||||
The `append($stream, $callback, $read_write = STREAM_FILTER_ALL)` function can be used to
|
||||
append a filter callback to the given stream.
|
||||
|
||||
Each stream can have a list of filters attached.
|
||||
This function appends a filter to the end of this list.
|
||||
|
||||
This function returns a filter resource which can be passed to [`remove()`](#remove).
|
||||
If the given filter can not be added, it throws an `Exception`.
|
||||
|
||||
The `$stream` can be any valid stream resource, such as:
|
||||
|
||||
```php
|
||||
$stream = fopen('demo.txt', 'w+');
|
||||
```
|
||||
|
||||
The `$callback` should be a valid callable function which accepts an individual chunk of data
|
||||
and should return the updated chunk:
|
||||
|
||||
```php
|
||||
$filter = Filter\append($stream, function ($chunk) {
|
||||
// will be called each time you read or write a $chunk to/from the stream
|
||||
return $chunk;
|
||||
});
|
||||
```
|
||||
|
||||
As such, you can also use native PHP functions or any other `callable`:
|
||||
|
||||
```php
|
||||
Filter\append($stream, 'strtoupper');
|
||||
|
||||
// will write "HELLO" to the underlying stream
|
||||
fwrite($stream, 'hello');
|
||||
```
|
||||
|
||||
If the `$callback` accepts invocation without parameters, then this signature
|
||||
will be invoked once ending (flushing) the filter:
|
||||
|
||||
```php
|
||||
Filter\append($stream, function ($chunk = null) {
|
||||
if ($chunk === null) {
|
||||
// will be called once ending the filter
|
||||
return 'end';
|
||||
}
|
||||
// will be called each time you read or write a $chunk to/from the stream
|
||||
return $chunk;
|
||||
});
|
||||
|
||||
fclose($stream);
|
||||
```
|
||||
|
||||
> Note: Legacy PHP versions (PHP < 5.4) do not support passing additional data
|
||||
from the end signal handler if the stream is being closed.
|
||||
|
||||
If your callback throws an `Exception`, then the filter process will be aborted.
|
||||
In order to play nice with PHP's stream handling, the `Exception` will be
|
||||
transformed to a PHP warning instead:
|
||||
|
||||
```php
|
||||
Filter\append($stream, function ($chunk) {
|
||||
throw new \RuntimeException('Unexpected chunk');
|
||||
});
|
||||
|
||||
// raises an E_USER_WARNING with "Error invoking filter: Unexpected chunk"
|
||||
fwrite($stream, 'hello');
|
||||
```
|
||||
|
||||
The optional `$read_write` parameter can be used to only invoke the `$callback` when either writing to the stream or only when reading from the stream:
|
||||
|
||||
```php
|
||||
Filter\append($stream, function ($chunk) {
|
||||
// will be called each time you write to the stream
|
||||
return $chunk;
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
Filter\append($stream, function ($chunk) {
|
||||
// will be called each time you read from the stream
|
||||
return $chunk;
|
||||
}, STREAM_FILTER_READ);
|
||||
```
|
||||
|
||||
> Note that once a filter has been added to stream, the stream can no longer be passed to
|
||||
> [`stream_select()`](http://php.net/manual/en/function.stream-select.php)
|
||||
> (and family).
|
||||
>
|
||||
> > Warning: stream_select(): cannot cast a filtered stream on this system in {file} on line {line}
|
||||
>
|
||||
> This is due to limitations of PHP's stream filter support, as it can no longer reliably
|
||||
> tell when the underlying stream resource is actually ready.
|
||||
> As an alternative, consider calling `stream_select()` on the unfiltered stream and
|
||||
> then pass the unfiltered data through the [`fun()`](#fun) function.
|
||||
|
||||
### prepend()
|
||||
|
||||
The `prepend($stream, $callback, $read_write = STREAM_FILTER_ALL)` function can be used to
|
||||
prepend a filter callback to the given stream.
|
||||
|
||||
Each stream can have a list of filters attached.
|
||||
This function prepends a filter to the start of this list.
|
||||
|
||||
This function returns a filter resource which can be passed to [`remove()`](#remove).
|
||||
If the given filter can not be added, it throws an `Exception`.
|
||||
|
||||
```php
|
||||
$filter = Filter\prepend($stream, function ($chunk) {
|
||||
// will be called each time you read or write a $chunk to/from the stream
|
||||
return $chunk;
|
||||
});
|
||||
```
|
||||
|
||||
Except for the position in the list of filters, this function behaves exactly
|
||||
like the [`append()`](#append) function.
|
||||
For more details about its behavior, see also the [`append()`](#append) function.
|
||||
|
||||
### fun()
|
||||
|
||||
The `fun($filter, $parameters = null)` function can be used to
|
||||
create a filter function which uses the given built-in `$filter`.
|
||||
|
||||
PHP comes with a useful set of [built-in filters](http://php.net/manual/en/filters.php).
|
||||
Using `fun()` makes accessing these as easy as passing an input string to filter
|
||||
and getting the filtered output string.
|
||||
|
||||
```php
|
||||
$fun = Filter\fun('string.rot13');
|
||||
|
||||
assert('grfg' === $fun('test'));
|
||||
assert('test' === $fun($fun('test'));
|
||||
```
|
||||
|
||||
Please note that not all filter functions may be available depending on installed
|
||||
PHP extensions and the PHP version in use.
|
||||
In particular, [HHVM](http://hhvm.com/) may not offer the same filter functions
|
||||
or parameters as Zend PHP.
|
||||
Accessing an unknown filter function will result in a `RuntimeException`:
|
||||
|
||||
```php
|
||||
Filter\fun('unknown'); // throws RuntimeException
|
||||
```
|
||||
|
||||
Some filters may accept or require additional filter parameters – most
|
||||
filters do not require filter parameters.
|
||||
If given, the optional `$parameters` argument will be passed to the
|
||||
underlying filter handler as-is.
|
||||
In particular, note how *not passing* this parameter at all differs from
|
||||
explicitly passing a `null` value (which many filters do not accept).
|
||||
Please refer to the individual filter definition for more details.
|
||||
For example, the `string.strip_tags` filter can be invoked like this:
|
||||
|
||||
```php
|
||||
$fun = Filter\fun('string.strip_tags', '<a><b>');
|
||||
|
||||
$ret = $fun('<b>h<br>i</b>');
|
||||
assert('<b>hi</b>' === $ret);
|
||||
```
|
||||
|
||||
Under the hood, this function allocates a temporary memory stream, so it's
|
||||
recommended to clean up the filter function after use.
|
||||
Also, some filter functions (in particular the
|
||||
[zlib compression filters](http://php.net/manual/en/filters.compression.php))
|
||||
may use internal buffers and may emit a final data chunk on close.
|
||||
The filter function can be closed by invoking without any arguments:
|
||||
|
||||
```php
|
||||
$fun = Filter\fun('zlib.deflate');
|
||||
|
||||
$ret = $fun('hello') . $fun('world') . $fun();
|
||||
assert('helloworld' === gzinflate($ret));
|
||||
```
|
||||
|
||||
The filter function must not be used anymore after it has been closed.
|
||||
Doing so will result in a `RuntimeException`:
|
||||
|
||||
```php
|
||||
$fun = Filter\fun('string.rot13');
|
||||
$fun();
|
||||
|
||||
$fun('test'); // throws RuntimeException
|
||||
```
|
||||
|
||||
> Note: If you're using the zlib compression filters, then you should be wary
|
||||
about engine inconsistencies between different PHP versions and HHVM.
|
||||
These inconsistencies exist in the underlying PHP engines and there's little we
|
||||
can do about this in this library.
|
||||
[Our test suite](tests/) contains several test cases that exhibit these issues.
|
||||
If you feel some test case is missing or outdated, we're happy to accept PRs! :)
|
||||
|
||||
### remove()
|
||||
|
||||
The `remove($filter)` function can be used to
|
||||
remove a filter previously added via [`append()`](#append) or [`prepend()`](#prepend).
|
||||
|
||||
```php
|
||||
$filter = Filter\append($stream, function () {
|
||||
// …
|
||||
});
|
||||
Filter\remove($filter);
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
The recommended way to install this library is [through Composer](https://getcomposer.org).
|
||||
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
|
||||
|
||||
This will install the latest supported version:
|
||||
|
||||
```bash
|
||||
$ composer require clue/stream-filter:^1.4
|
||||
```
|
||||
|
||||
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
|
||||
|
||||
This project aims to run on any platform and thus does not require any PHP
|
||||
extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
|
||||
HHVM.
|
||||
It's *highly recommended to use PHP 7+* for this project.
|
||||
Older PHP versions may suffer from a number of inconsistencies documented above.
|
||||
|
||||
## Tests
|
||||
|
||||
To run the test suite, you first need to clone this repo and then install all
|
||||
dependencies [through Composer](http://getcomposer.org):
|
||||
|
||||
```bash
|
||||
$ composer install
|
||||
```
|
||||
|
||||
To run the test suite, go to the project root and run:
|
||||
|
||||
```bash
|
||||
$ php vendor/bin/phpunit
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "clue/stream-filter",
|
||||
"description": "A simple and modern approach to stream filtering in PHP",
|
||||
"keywords": ["stream", "callback", "filter", "php_user_filter", "stream_filter_append", "stream_filter_register", "bucket brigade"],
|
||||
"homepage": "https://github.com/clue/php-stream-filter",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Lück",
|
||||
"email": "christian@lueck.tv"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.0 || ^4.8"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Clue\\StreamFilter\\": "src/" },
|
||||
"files": [ "src/functions.php" ]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
// $ echo test | php examples/base64_encode.php | php examples/base64_decode.php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// decoding requires buffering in chunks of 4 bytes each
|
||||
$buffer = '';
|
||||
Clue\StreamFilter\append(STDIN, function ($chunk = null) use (&$buffer) {
|
||||
if ($chunk === null) {
|
||||
if (strlen($buffer) % 4 !== 0) {
|
||||
throw new \UnexpectedValueException('Invalid length');
|
||||
}
|
||||
$chunk = $buffer;
|
||||
} else {
|
||||
$buffer .= $chunk;
|
||||
$len = strlen($buffer) - (strlen($buffer) % 4);
|
||||
$chunk = (string)substr($buffer, 0, $len);
|
||||
$buffer = (string)substr($buffer, $len);
|
||||
}
|
||||
|
||||
$ret = base64_decode($chunk, true);
|
||||
if ($ret === false) {
|
||||
throw new \UnexpectedValueException('Not a valid base64 encoded chunk');
|
||||
}
|
||||
return $ret;
|
||||
}, STREAM_FILTER_READ);
|
||||
|
||||
fpassthru(STDIN);
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
// $ echo test | php examples/base64_encode.php | base64 --decode
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// encoding requires buffering in chunks of 3 bytes each
|
||||
$buffer = '';
|
||||
Clue\StreamFilter\append(STDIN, function ($chunk = null) use (&$buffer) {
|
||||
if ($chunk === null) {
|
||||
return base64_encode($buffer);
|
||||
}
|
||||
$buffer .= $chunk;
|
||||
$len = strlen($buffer) - (strlen($buffer) % 3);
|
||||
$chunk = substr($buffer, 0, $len);
|
||||
$buffer = substr($buffer, $len);
|
||||
|
||||
return base64_encode($chunk);
|
||||
}, STREAM_FILTER_READ);
|
||||
|
||||
fpassthru(STDIN);
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// $ echo test | php examples/uppercase.php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
Clue\StreamFilter\append(STDIN, 'strtoupper');
|
||||
|
||||
fpassthru(STDIN);
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite>
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\StreamFilter;
|
||||
|
||||
use php_user_filter;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionFunction;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
*
|
||||
* @internal
|
||||
* @see append()
|
||||
* @see prepend()
|
||||
*/
|
||||
class CallbackFilter extends php_user_filter
|
||||
{
|
||||
private $callback;
|
||||
private $closed = true;
|
||||
private $supportsClose = false;
|
||||
|
||||
public function onCreate()
|
||||
{
|
||||
$this->closed = false;
|
||||
|
||||
if (!is_callable($this->params)) {
|
||||
throw new InvalidArgumentException('No valid callback parameter given to stream_filter_(append|prepend)');
|
||||
}
|
||||
$this->callback = $this->params;
|
||||
|
||||
// callback supports end event if it accepts invocation without arguments
|
||||
$ref = new ReflectionFunction($this->callback);
|
||||
$this->supportsClose = ($ref->getNumberOfRequiredParameters() === 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onClose()
|
||||
{
|
||||
$this->closed = true;
|
||||
|
||||
// callback supports closing and is not already closed
|
||||
if ($this->supportsClose) {
|
||||
$this->supportsClose = false;
|
||||
// invoke without argument to signal end and discard resulting buffer
|
||||
try {
|
||||
call_user_func($this->callback);
|
||||
} catch (Exception $ignored) {
|
||||
// this might be called during engine shutdown, so it's not safe
|
||||
// to raise any errors or exceptions here
|
||||
// trigger_error('Error closing filter: ' . $ignored->getMessage(), E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
$this->callback = null;
|
||||
}
|
||||
|
||||
public function filter($in, $out, &$consumed, $closing)
|
||||
{
|
||||
// concatenate whole buffer from input brigade
|
||||
$data = '';
|
||||
while ($bucket = stream_bucket_make_writeable($in)) {
|
||||
$consumed += $bucket->datalen;
|
||||
$data .= $bucket->data;
|
||||
}
|
||||
|
||||
// skip processing callback that already ended
|
||||
if ($this->closed) {
|
||||
return PSFS_FEED_ME;
|
||||
}
|
||||
|
||||
// only invoke filter function if buffer is not empty
|
||||
// this may skip flushing a closing filter
|
||||
if ($data !== '') {
|
||||
try {
|
||||
$data = call_user_func($this->callback, $data);
|
||||
} catch (Exception $e) {
|
||||
// exception should mark filter as closed
|
||||
$this->onClose();
|
||||
trigger_error('Error invoking filter: ' . $e->getMessage(), E_USER_WARNING);
|
||||
|
||||
return PSFS_ERR_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
// mark filter as closed after processing closing chunk
|
||||
if ($closing) {
|
||||
$this->closed = true;
|
||||
|
||||
// callback supports closing and is not already closed
|
||||
if ($this->supportsClose) {
|
||||
$this->supportsClose = false;
|
||||
|
||||
// invoke without argument to signal end and append resulting buffer
|
||||
try {
|
||||
$data .= call_user_func($this->callback);
|
||||
} catch (Exception $e) {
|
||||
trigger_error('Error ending filter: ' . $e->getMessage(), E_USER_WARNING);
|
||||
|
||||
return PSFS_ERR_FATAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($data !== '') {
|
||||
// create a new bucket for writing the resulting buffer to the output brigade
|
||||
// reusing an existing bucket turned out to be bugged in some environments (ancient PHP versions and HHVM)
|
||||
$bucket = @stream_bucket_new($this->stream, $data);
|
||||
|
||||
// legacy PHP versions (PHP < 5.4) do not support passing data from the event signal handler
|
||||
// because closing the stream invalidates the stream and its stream bucket brigade before
|
||||
// invoking the filter close handler.
|
||||
if ($bucket !== false) {
|
||||
stream_bucket_append($out, $bucket);
|
||||
}
|
||||
}
|
||||
|
||||
return PSFS_PASS_ON;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\StreamFilter;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* append a callback filter to the given stream
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param callable $callback
|
||||
* @param int $read_write
|
||||
* @return resource filter resource which can be used for `remove()`
|
||||
* @throws Exception on error
|
||||
* @uses stream_filter_append()
|
||||
*/
|
||||
function append($stream, $callback, $read_write = STREAM_FILTER_ALL)
|
||||
{
|
||||
$ret = @stream_filter_append($stream, register(), $read_write, $callback);
|
||||
|
||||
if ($ret === false) {
|
||||
$error = error_get_last() + array('message' => '');
|
||||
throw new RuntimeException('Unable to append filter: ' . $error['message']);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepend a callback filter to the given stream
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param callable $callback
|
||||
* @param int $read_write
|
||||
* @return resource filter resource which can be used for `remove()`
|
||||
* @throws Exception on error
|
||||
* @uses stream_filter_prepend()
|
||||
*/
|
||||
function prepend($stream, $callback, $read_write = STREAM_FILTER_ALL)
|
||||
{
|
||||
$ret = @stream_filter_prepend($stream, register(), $read_write, $callback);
|
||||
|
||||
if ($ret === false) {
|
||||
$error = error_get_last() + array('message' => '');
|
||||
throw new RuntimeException('Unable to prepend filter: ' . $error['message']);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates filter fun (function) which uses the given built-in $filter
|
||||
*
|
||||
* Some filters may accept or require additional filter parameters – most
|
||||
* filters do not require filter parameters.
|
||||
* If given, the optional `$parameters` argument will be passed to the
|
||||
* underlying filter handler as-is.
|
||||
* In particular, note how *not passing* this parameter at all differs from
|
||||
* explicitly passing a `null` value (which many filters do not accept).
|
||||
* Please refer to the individual filter definition for more details.
|
||||
*
|
||||
* @param string $filter built-in filter name. See stream_get_filters() or http://php.net/manual/en/filters.php
|
||||
* @param mixed $parameters (optional) parameters to pass to the built-in filter as-is
|
||||
* @return callable a filter callback which can be append()'ed or prepend()'ed
|
||||
* @throws RuntimeException on error
|
||||
* @link http://php.net/manual/en/filters.php
|
||||
* @see stream_get_filters()
|
||||
* @see append()
|
||||
*/
|
||||
function fun($filter, $parameters = null)
|
||||
{
|
||||
$fp = fopen('php://memory', 'w');
|
||||
if (func_num_args() === 1) {
|
||||
$filter = @stream_filter_append($fp, $filter, STREAM_FILTER_WRITE);
|
||||
} else {
|
||||
$filter = @stream_filter_append($fp, $filter, STREAM_FILTER_WRITE, $parameters);
|
||||
}
|
||||
|
||||
if ($filter === false) {
|
||||
fclose($fp);
|
||||
$error = error_get_last() + array('message' => '');
|
||||
throw new RuntimeException('Unable to access built-in filter: ' . $error['message']);
|
||||
}
|
||||
|
||||
// append filter function which buffers internally
|
||||
$buffer = '';
|
||||
append($fp, function ($chunk) use (&$buffer) {
|
||||
$buffer .= $chunk;
|
||||
|
||||
// always return empty string in order to skip actually writing to stream resource
|
||||
return '';
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
$closed = false;
|
||||
|
||||
return function ($chunk = null) use ($fp, $filter, &$buffer, &$closed) {
|
||||
if ($closed) {
|
||||
throw new \RuntimeException('Unable to perform operation on closed stream');
|
||||
}
|
||||
if ($chunk === null) {
|
||||
$closed = true;
|
||||
$buffer = '';
|
||||
fclose($fp);
|
||||
return $buffer;
|
||||
}
|
||||
// initialize buffer and invoke filters by attempting to write to stream
|
||||
$buffer = '';
|
||||
fwrite($fp, $chunk);
|
||||
|
||||
// buffer now contains everything the filter function returned
|
||||
return $buffer;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a callback filter from the given stream
|
||||
*
|
||||
* @param resource $filter
|
||||
* @return boolean true on success or false on error
|
||||
* @throws Exception on error
|
||||
* @uses stream_filter_remove()
|
||||
*/
|
||||
function remove($filter)
|
||||
{
|
||||
if (@stream_filter_remove($filter) === false) {
|
||||
throw new RuntimeException('Unable to remove given filter');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* registers the callback filter and returns the resulting filter name
|
||||
*
|
||||
* There should be little reason to call this function manually.
|
||||
*
|
||||
* @return string filter name
|
||||
* @uses CallbackFilter
|
||||
*/
|
||||
function register()
|
||||
{
|
||||
static $registered = null;
|
||||
if ($registered === null) {
|
||||
$registered = 'stream-callback';
|
||||
stream_filter_register($registered, __NAMESPACE__ . '\CallbackFilter');
|
||||
}
|
||||
return $registered;
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
use Clue\StreamFilter;
|
||||
|
||||
class FilterTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testAppendSimpleCallback()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
StreamFilter\append($stream, function ($chunk) {
|
||||
return strtoupper($chunk);
|
||||
});
|
||||
|
||||
fwrite($stream, 'hello');
|
||||
fwrite($stream, 'world');
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals('HELLOWORLD', stream_get_contents($stream));
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testAppendNativePhpFunction()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
StreamFilter\append($stream, 'strtoupper');
|
||||
|
||||
fwrite($stream, 'hello');
|
||||
fwrite($stream, 'world');
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals('HELLOWORLD', stream_get_contents($stream));
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testAppendChangingChunkSize()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
StreamFilter\append($stream, function ($chunk) {
|
||||
return str_replace(array('a','e','i','o','u'), '', $chunk);
|
||||
});
|
||||
|
||||
fwrite($stream, 'hello');
|
||||
fwrite($stream, 'world');
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals('hllwrld', stream_get_contents($stream));
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testAppendReturningEmptyStringWillNotPassThrough()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
StreamFilter\append($stream, function ($chunk) {
|
||||
return '';
|
||||
});
|
||||
|
||||
fwrite($stream, 'hello');
|
||||
fwrite($stream, 'world');
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals('', stream_get_contents($stream));
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testAppendEndEventCanBeBufferedOnClose()
|
||||
{
|
||||
if (PHP_VERSION < 5.4) $this->markTestSkipped('Not supported on legacy PHP');
|
||||
|
||||
$stream = $this->createStream();
|
||||
|
||||
StreamFilter\append($stream, function ($chunk = null) {
|
||||
if ($chunk === null) {
|
||||
// this signals the end event
|
||||
return '!';
|
||||
}
|
||||
return $chunk . ' ';
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
$buffered = '';
|
||||
StreamFilter\append($stream, function ($chunk) use (&$buffered) {
|
||||
$buffered .= $chunk;
|
||||
return '';
|
||||
});
|
||||
|
||||
fwrite($stream, 'hello');
|
||||
fwrite($stream, 'world');
|
||||
|
||||
fclose($stream);
|
||||
|
||||
$this->assertEquals('hello world !', $buffered);
|
||||
}
|
||||
|
||||
public function testAppendEndEventWillBeCalledOnRemove()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
$ended = false;
|
||||
$filter = StreamFilter\append($stream, function ($chunk = null) use (&$ended) {
|
||||
if ($chunk === null) {
|
||||
$ended = true;
|
||||
}
|
||||
return $chunk;
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
$this->assertEquals(0, $ended);
|
||||
StreamFilter\remove($filter);
|
||||
$this->assertEquals(1, $ended);
|
||||
}
|
||||
|
||||
public function testAppendEndEventWillBeCalledOnClose()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
$ended = false;
|
||||
StreamFilter\append($stream, function ($chunk = null) use (&$ended) {
|
||||
if ($chunk === null) {
|
||||
$ended = true;
|
||||
}
|
||||
return $chunk;
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
$this->assertEquals(0, $ended);
|
||||
fclose($stream);
|
||||
$this->assertEquals(1, $ended);
|
||||
}
|
||||
|
||||
public function testAppendWriteOnly()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
$invoked = 0;
|
||||
|
||||
StreamFilter\append($stream, function ($chunk) use (&$invoked) {
|
||||
++$invoked;
|
||||
|
||||
return $chunk;
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
fwrite($stream, 'a');
|
||||
fwrite($stream, 'b');
|
||||
fwrite($stream, 'c');
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals(3, $invoked);
|
||||
$this->assertEquals('abc', stream_get_contents($stream));
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testAppendReadOnly()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
$invoked = 0;
|
||||
|
||||
StreamFilter\append($stream, function ($chunk) use (&$invoked) {
|
||||
++$invoked;
|
||||
|
||||
return $chunk;
|
||||
}, STREAM_FILTER_READ);
|
||||
|
||||
fwrite($stream, 'a');
|
||||
fwrite($stream, 'b');
|
||||
fwrite($stream, 'c');
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals(0, $invoked);
|
||||
$this->assertEquals('abc', stream_get_contents($stream));
|
||||
$this->assertEquals(1, $invoked);
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testOrderCallingAppendAfterPrepend()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
StreamFilter\append($stream, function ($chunk) {
|
||||
return '[' . $chunk . ']';
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
StreamFilter\prepend($stream, function ($chunk) {
|
||||
return '(' . $chunk . ')';
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
fwrite($stream, 'hello');
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals('[(hello)]', stream_get_contents($stream));
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testRemoveFilter()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
$first = StreamFilter\append($stream, function ($chunk) {
|
||||
return $chunk . '?';
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
StreamFilter\append($stream, function ($chunk) {
|
||||
return $chunk . '!';
|
||||
}, STREAM_FILTER_WRITE);
|
||||
|
||||
StreamFilter\remove($first);
|
||||
|
||||
fwrite($stream, 'hello');
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals('hello!', stream_get_contents($stream));
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testAppendFunDechunk()
|
||||
{
|
||||
if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (dechunk filter does not exist)');
|
||||
|
||||
$stream = $this->createStream();
|
||||
|
||||
StreamFilter\append($stream, StreamFilter\fun('dechunk'), STREAM_FILTER_WRITE);
|
||||
|
||||
fwrite($stream, "2\r\nhe\r\n");
|
||||
fwrite($stream, "3\r\nllo\r\n");
|
||||
fwrite($stream, "0\r\n\r\n");
|
||||
rewind($stream);
|
||||
|
||||
$this->assertEquals('hello', stream_get_contents($stream));
|
||||
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
public function testAppendThrows()
|
||||
{
|
||||
$this->createErrorHandler($errors);
|
||||
|
||||
$stream = $this->createStream();
|
||||
$this->createErrorHandler($errors);
|
||||
|
||||
StreamFilter\append($stream, function ($chunk) {
|
||||
throw new \DomainException($chunk);
|
||||
});
|
||||
|
||||
fwrite($stream, 'test');
|
||||
|
||||
$this->removeErrorHandler();
|
||||
$this->assertCount(1, $errors);
|
||||
$this->assertContains('test', $errors[0]);
|
||||
}
|
||||
|
||||
public function testAppendThrowsDuringEnd()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
$this->createErrorHandler($errors);
|
||||
|
||||
StreamFilter\append($stream, function ($chunk = null) {
|
||||
if ($chunk === null) {
|
||||
throw new \DomainException('end');
|
||||
}
|
||||
return $chunk;
|
||||
});
|
||||
|
||||
fclose($stream);
|
||||
|
||||
$this->removeErrorHandler();
|
||||
|
||||
// We can only assert we're not seeing an exception here…
|
||||
// * php 5.3-5.6 sees one error here
|
||||
// * php 7 does not see any error here
|
||||
// * hhvm sees the same error twice
|
||||
//
|
||||
// If you're curious:
|
||||
//
|
||||
// var_dump($errors);
|
||||
// $this->assertCount(1, $errors);
|
||||
// $this->assertContains('end', $errors[0]);
|
||||
}
|
||||
|
||||
public function testAppendThrowsShouldTriggerEnd()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
$this->createErrorHandler($errors);
|
||||
|
||||
$ended = false;
|
||||
StreamFilter\append($stream, function ($chunk = null) use (&$ended) {
|
||||
if ($chunk === null) {
|
||||
$ended = true;
|
||||
return '';
|
||||
}
|
||||
throw new \DomainException($chunk);
|
||||
});
|
||||
|
||||
$this->assertEquals(false, $ended);
|
||||
fwrite($stream, 'test');
|
||||
$this->assertEquals(true, $ended);
|
||||
|
||||
$this->removeErrorHandler();
|
||||
$this->assertCount(1, $errors);
|
||||
$this->assertContains('test', $errors[0]);
|
||||
}
|
||||
|
||||
public function testAppendThrowsShouldTriggerEndButIgnoreExceptionDuringEnd()
|
||||
{
|
||||
//$this->markTestIncomplete();
|
||||
$stream = $this->createStream();
|
||||
$this->createErrorHandler($errors);
|
||||
|
||||
StreamFilter\append($stream, function ($chunk = null) {
|
||||
if ($chunk === null) {
|
||||
$chunk = 'end';
|
||||
//return '';
|
||||
}
|
||||
throw new \DomainException($chunk);
|
||||
});
|
||||
|
||||
fwrite($stream, 'test');
|
||||
|
||||
$this->removeErrorHandler();
|
||||
$this->assertCount(1, $errors);
|
||||
$this->assertContains('test', $errors[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testAppendInvalidStreamIsRuntimeError()
|
||||
{
|
||||
if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid stream)');
|
||||
StreamFilter\append(false, function () { });
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testPrependInvalidStreamIsRuntimeError()
|
||||
{
|
||||
if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid stream)');
|
||||
StreamFilter\prepend(false, function () { });
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testRemoveInvalidFilterIsRuntimeError()
|
||||
{
|
||||
if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid filters)');
|
||||
StreamFilter\remove(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidCallbackIsInvalidArgument()
|
||||
{
|
||||
$stream = $this->createStream();
|
||||
|
||||
StreamFilter\append($stream, 'a-b-c');
|
||||
}
|
||||
|
||||
private function createStream()
|
||||
{
|
||||
return fopen('php://memory', 'r+');
|
||||
}
|
||||
|
||||
private function createErrorHandler(&$errors)
|
||||
{
|
||||
$errors = array();
|
||||
set_error_handler(function ($_, $message) use (&$errors) {
|
||||
$errors []= $message;
|
||||
});
|
||||
}
|
||||
|
||||
private function removeErrorHandler()
|
||||
{
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Clue\StreamFilter as Filter;
|
||||
|
||||
class FunTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testFunInRot13()
|
||||
{
|
||||
$rot = Filter\fun('string.rot13');
|
||||
|
||||
$this->assertEquals('grfg', $rot('test'));
|
||||
$this->assertEquals('test', $rot($rot('test')));
|
||||
$this->assertEquals(null, $rot());
|
||||
}
|
||||
|
||||
public function testFunInQuotedPrintable()
|
||||
{
|
||||
$encode = Filter\fun('convert.quoted-printable-encode');
|
||||
$decode = Filter\fun('convert.quoted-printable-decode');
|
||||
|
||||
$this->assertEquals('t=C3=A4st', $encode('täst'));
|
||||
$this->assertEquals('täst', $decode($encode('täst')));
|
||||
$this->assertEquals(null, $encode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testFunWriteAfterCloseRot13()
|
||||
{
|
||||
$rot = Filter\fun('string.rot13');
|
||||
|
||||
$this->assertEquals(null, $rot());
|
||||
$rot('test');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testFunInvalid()
|
||||
{
|
||||
Filter\fun('unknown');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
use Clue\StreamFilter;
|
||||
|
||||
class BuiltInZlibTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testFunZlibDeflateHelloWorld()
|
||||
{
|
||||
$deflate = StreamFilter\fun('zlib.deflate');
|
||||
|
||||
$data = $deflate('hello') . $deflate(' ') . $deflate('world') . $deflate();
|
||||
|
||||
$this->assertEquals(gzdeflate('hello world'), $data);
|
||||
}
|
||||
|
||||
public function testFunZlibDeflateEmpty()
|
||||
{
|
||||
if (PHP_VERSION >= 7) $this->markTestSkipped('Not supported on PHP7 (empty string does not invoke filter)');
|
||||
|
||||
$deflate = StreamFilter\fun('zlib.deflate');
|
||||
|
||||
//$data = gzdeflate('');
|
||||
$data = $deflate();
|
||||
|
||||
$this->assertEquals("\x03\x00", $data);
|
||||
}
|
||||
|
||||
public function testFunZlibDeflateBig()
|
||||
{
|
||||
$deflate = StreamFilter\fun('zlib.deflate');
|
||||
|
||||
$n = 1000;
|
||||
$expected = str_repeat('hello', $n);
|
||||
|
||||
$bytes = '';
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$bytes .= $deflate('hello');
|
||||
}
|
||||
$bytes .= $deflate();
|
||||
|
||||
$this->assertEquals($expected, gzinflate($bytes));
|
||||
}
|
||||
|
||||
public function testFunZlibInflateHelloWorld()
|
||||
{
|
||||
$inflate = StreamFilter\fun('zlib.inflate');
|
||||
|
||||
$data = $inflate(gzdeflate('hello world')) . $inflate();
|
||||
|
||||
$this->assertEquals('hello world', $data);
|
||||
}
|
||||
|
||||
public function testFunZlibInflateEmpty()
|
||||
{
|
||||
$inflate = StreamFilter\fun('zlib.inflate');
|
||||
|
||||
$data = $inflate("\x03\x00") . $inflate();
|
||||
|
||||
$this->assertEquals('', $data);
|
||||
}
|
||||
|
||||
public function testFunZlibInflateBig()
|
||||
{
|
||||
if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (final chunk will not be emitted)');
|
||||
|
||||
$inflate = StreamFilter\fun('zlib.inflate');
|
||||
|
||||
$expected = str_repeat('hello', 10);
|
||||
$bytes = gzdeflate($expected);
|
||||
|
||||
$ret = '';
|
||||
foreach (str_split($bytes, 2) as $chunk) {
|
||||
$ret .= $inflate($chunk);
|
||||
}
|
||||
$ret .= $inflate();
|
||||
|
||||
$this->assertEquals($expected, $ret);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ return array(
|
||||
'Locale' => $vendorDir . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/Locale.php',
|
||||
'NumberFormatter' => $vendorDir . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php',
|
||||
'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
|
||||
'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
|
||||
'SqlFormatter' => $vendorDir . '/jdorn/sql-formatter/lib/SqlFormatter.php',
|
||||
'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
|
||||
);
|
||||
|
||||
@@ -6,15 +6,20 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'3a37ebac017bc098e9a86b35401e7a68' => $vendorDir . '/mongodb/mongodb/src/functions.php',
|
||||
'06dd8487319ccd8403765f5b8c9f2d61' => $vendorDir . '/alcaeus/mongo-php-adapter/lib/Mongo/functions.php',
|
||||
'92c8763cd6170fce6fcfe7e26b4e8c10' => $vendorDir . '/symfony/phpunit-bridge/bootstrap.php',
|
||||
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'ddc0a4d7e61c0286f0f8593b1903e894' => $vendorDir . '/clue/stream-filter/src/functions.php',
|
||||
'8cff32064859f4559445b89279f3199c' => $vendorDir . '/php-http/message/src/filters.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
|
||||
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
|
||||
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
|
||||
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
|
||||
'6a47392539ca2329373e0d33e1dba053' => $vendorDir . '/symfony/polyfill-intl-icu/bootstrap.php',
|
||||
'32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
|
||||
'3a37ebac017bc098e9a86b35401e7a68' => $vendorDir . '/mongodb/mongodb/src/functions.php',
|
||||
'ce89ac35a6c330c55f4710717db9ff78' => $vendorDir . '/kriswallsmith/assetic/src/functions.php',
|
||||
'06dd8487319ccd8403765f5b8c9f2d61' => $vendorDir . '/alcaeus/mongo-php-adapter/lib/Mongo/functions.php',
|
||||
);
|
||||
|
||||
@@ -19,7 +19,6 @@ return array(
|
||||
'Doctrine\\MongoDB' => array($vendorDir . '/doctrine/mongodb/lib'),
|
||||
'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/lib'),
|
||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib'),
|
||||
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib'),
|
||||
'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'),
|
||||
'Assetic' => array($vendorDir . '/kriswallsmith/assetic/src'),
|
||||
'Alom' => array($vendorDir . '/alom/graphviz/src'),
|
||||
|
||||
+18
-1
@@ -9,18 +9,19 @@ return array(
|
||||
'Zend\\EventManager\\' => array($vendorDir . '/zendframework/zend-eventmanager/src'),
|
||||
'Zend\\Code\\' => array($vendorDir . '/zendframework/zend-code/src'),
|
||||
'Vimeo\\' => array($vendorDir . '/vimeo/vimeo-api/src/Vimeo'),
|
||||
'Twig\\' => array($vendorDir . '/twig/twig/src'),
|
||||
'Tests\\' => array($baseDir . '/tests'),
|
||||
'Symfony\\Polyfill\\Util\\' => array($vendorDir . '/symfony/polyfill-util'),
|
||||
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
|
||||
'Symfony\\Polyfill\\Php56\\' => array($vendorDir . '/symfony/polyfill-php56'),
|
||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||
'Symfony\\Polyfill\\Apcu\\' => array($vendorDir . '/symfony/polyfill-apcu'),
|
||||
'Symfony\\Component\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Component'),
|
||||
'Symfony\\Bundle\\SwiftmailerBundle\\' => array($vendorDir . '/symfony/swiftmailer-bundle'),
|
||||
'Symfony\\Bundle\\MonologBundle\\' => array($vendorDir . '/symfony/monolog-bundle'),
|
||||
'Symfony\\Bundle\\AsseticBundle\\' => array($vendorDir . '/symfony/assetic-bundle'),
|
||||
'Symfony\\Bundle\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bundle'),
|
||||
'Symfony\\Bridge\\Twig\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/Twig'),
|
||||
'Symfony\\Bridge\\Swiftmailer\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/Swiftmailer'),
|
||||
'Symfony\\Bridge\\ProxyManager\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/ProxyManager'),
|
||||
'Symfony\\Bridge\\PhpUnit\\' => array($vendorDir . '/symfony/phpunit-bridge'),
|
||||
'Symfony\\Bridge\\Monolog\\' => array($vendorDir . '/symfony/symfony/src/Symfony/Bridge/Monolog'),
|
||||
@@ -29,6 +30,7 @@ return array(
|
||||
'Sensio\\Bundle\\FrameworkExtraBundle\\' => array($vendorDir . '/sensio/framework-extra-bundle'),
|
||||
'Sensio\\Bundle\\DistributionBundle\\' => array($vendorDir . '/sensio/distribution-bundle'),
|
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
||||
'PackageVersions\\' => array($vendorDir . '/ocramius/package-versions/src/PackageVersions'),
|
||||
'Nmure\\CrawlerDetectBundle\\' => array($vendorDir . '/nmure/crawler-detect-bundle'),
|
||||
@@ -37,6 +39,18 @@ return array(
|
||||
'Liip\\ImagineBundle\\' => array($vendorDir . '/liip/imagine-bundle'),
|
||||
'Jaybizzle\\CrawlerDetect\\' => array($vendorDir . '/jaybizzle/crawler-detect/src'),
|
||||
'Incenteev\\ParameterHandler\\' => array($vendorDir . '/incenteev/composer-parameter-handler'),
|
||||
'Http\\Promise\\' => array($vendorDir . '/php-http/promise/src'),
|
||||
'Http\\Message\\' => array($vendorDir . '/php-http/message-factory/src', $vendorDir . '/php-http/message/src'),
|
||||
'Http\\HttplugBundle\\' => array($vendorDir . '/php-http/httplug-bundle'),
|
||||
'Http\\Discovery\\' => array($vendorDir . '/php-http/discovery/src'),
|
||||
'Http\\Client\\Common\\Plugin\\' => array($vendorDir . '/php-http/stopwatch-plugin/src', $vendorDir . '/php-http/logger-plugin/src', $vendorDir . '/php-http/cache-plugin/src'),
|
||||
'Http\\Client\\Common\\' => array($vendorDir . '/php-http/client-common/src'),
|
||||
'Http\\Client\\' => array($vendorDir . '/php-http/httplug/src'),
|
||||
'Http\\Adapter\\Guzzle6\\' => array($vendorDir . '/php-http/guzzle6-adapter/src'),
|
||||
'HWI\\Bundle\\OAuthBundle\\' => array($vendorDir . '/hwi/oauth-bundle'),
|
||||
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
|
||||
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
|
||||
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
|
||||
'FOS\\UserBundle\\' => array($vendorDir . '/friendsofsymfony/user-bundle'),
|
||||
'FOS\\ElasticaBundle\\' => array($vendorDir . '/friendsofsymfony/elastica-bundle'),
|
||||
'Elastica\\' => array($vendorDir . '/ruflin/elastica/lib/Elastica'),
|
||||
@@ -45,6 +59,7 @@ return array(
|
||||
'Doctrine\\ORM\\' => array($vendorDir . '/doctrine/orm/lib/Doctrine/ORM'),
|
||||
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
|
||||
'Doctrine\\DBAL\\Migrations\\' => array($vendorDir . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations'),
|
||||
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'),
|
||||
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'),
|
||||
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
|
||||
'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib/Doctrine/Common'),
|
||||
@@ -52,6 +67,8 @@ return array(
|
||||
'Doctrine\\Bundle\\MigrationsBundle\\' => array($vendorDir . '/doctrine/doctrine-migrations-bundle'),
|
||||
'Doctrine\\Bundle\\DoctrineCacheBundle\\' => array($vendorDir . '/doctrine/doctrine-cache-bundle'),
|
||||
'Doctrine\\Bundle\\DoctrineBundle\\' => array($vendorDir . '/doctrine/doctrine-bundle'),
|
||||
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
|
||||
'Clue\\StreamFilter\\' => array($vendorDir . '/clue/stream-filter/src'),
|
||||
'Alcaeus\\MongoDbAdapter\\' => array($vendorDir . '/alcaeus/mongo-php-adapter/lib/Alcaeus/MongoDbAdapter'),
|
||||
'' => array($baseDir . '/src'),
|
||||
);
|
||||
|
||||
+110
-11
@@ -7,17 +7,22 @@ namespace Composer\Autoload;
|
||||
class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
{
|
||||
public static $files = array (
|
||||
'3a37ebac017bc098e9a86b35401e7a68' => __DIR__ . '/..' . '/mongodb/mongodb/src/functions.php',
|
||||
'06dd8487319ccd8403765f5b8c9f2d61' => __DIR__ . '/..' . '/alcaeus/mongo-php-adapter/lib/Mongo/functions.php',
|
||||
'92c8763cd6170fce6fcfe7e26b4e8c10' => __DIR__ . '/..' . '/symfony/phpunit-bridge/bootstrap.php',
|
||||
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'ddc0a4d7e61c0286f0f8593b1903e894' => __DIR__ . '/..' . '/clue/stream-filter/src/functions.php',
|
||||
'8cff32064859f4559445b89279f3199c' => __DIR__ . '/..' . '/php-http/message/src/filters.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php',
|
||||
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
|
||||
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
|
||||
'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
|
||||
'6a47392539ca2329373e0d33e1dba053' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/bootstrap.php',
|
||||
'32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
|
||||
'3a37ebac017bc098e9a86b35401e7a68' => __DIR__ . '/..' . '/mongodb/mongodb/src/functions.php',
|
||||
'ce89ac35a6c330c55f4710717db9ff78' => __DIR__ . '/..' . '/kriswallsmith/assetic/src/functions.php',
|
||||
'06dd8487319ccd8403765f5b8c9f2d61' => __DIR__ . '/..' . '/alcaeus/mongo-php-adapter/lib/Mongo/functions.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
@@ -32,6 +37,7 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
),
|
||||
'T' =>
|
||||
array (
|
||||
'Twig\\' => 5,
|
||||
'Tests\\' => 6,
|
||||
),
|
||||
'S' =>
|
||||
@@ -40,13 +46,13 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
'Symfony\\Polyfill\\Php70\\' => 23,
|
||||
'Symfony\\Polyfill\\Php56\\' => 23,
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
'Symfony\\Polyfill\\Apcu\\' => 22,
|
||||
'Symfony\\Component\\' => 18,
|
||||
'Symfony\\Bundle\\SwiftmailerBundle\\' => 33,
|
||||
'Symfony\\Bundle\\MonologBundle\\' => 29,
|
||||
'Symfony\\Bundle\\AsseticBundle\\' => 29,
|
||||
'Symfony\\Bundle\\' => 15,
|
||||
'Symfony\\Bridge\\Twig\\' => 20,
|
||||
'Symfony\\Bridge\\Swiftmailer\\' => 27,
|
||||
'Symfony\\Bridge\\ProxyManager\\' => 28,
|
||||
'Symfony\\Bridge\\PhpUnit\\' => 23,
|
||||
'Symfony\\Bridge\\Monolog\\' => 23,
|
||||
@@ -58,6 +64,7 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\Log\\' => 8,
|
||||
'Psr\\Http\\Message\\' => 17,
|
||||
'Psr\\Cache\\' => 10,
|
||||
'PackageVersions\\' => 16,
|
||||
),
|
||||
@@ -82,6 +89,24 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
'Incenteev\\ParameterHandler\\' => 27,
|
||||
),
|
||||
'H' =>
|
||||
array (
|
||||
'Http\\Promise\\' => 13,
|
||||
'Http\\Message\\' => 13,
|
||||
'Http\\HttplugBundle\\' => 19,
|
||||
'Http\\Discovery\\' => 15,
|
||||
'Http\\Client\\Common\\Plugin\\' => 26,
|
||||
'Http\\Client\\Common\\' => 19,
|
||||
'Http\\Client\\' => 12,
|
||||
'Http\\Adapter\\Guzzle6\\' => 21,
|
||||
'HWI\\Bundle\\OAuthBundle\\' => 23,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'GuzzleHttp\\Psr7\\' => 16,
|
||||
'GuzzleHttp\\Promise\\' => 19,
|
||||
'GuzzleHttp\\' => 11,
|
||||
),
|
||||
'F' =>
|
||||
array (
|
||||
'FOS\\UserBundle\\' => 15,
|
||||
@@ -98,6 +123,7 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
'Doctrine\\ORM\\' => 13,
|
||||
'Doctrine\\Instantiator\\' => 22,
|
||||
'Doctrine\\DBAL\\Migrations\\' => 25,
|
||||
'Doctrine\\Common\\Inflector\\' => 26,
|
||||
'Doctrine\\Common\\Cache\\' => 22,
|
||||
'Doctrine\\Common\\Annotations\\' => 28,
|
||||
'Doctrine\\Common\\' => 16,
|
||||
@@ -106,6 +132,11 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
'Doctrine\\Bundle\\DoctrineCacheBundle\\' => 36,
|
||||
'Doctrine\\Bundle\\DoctrineBundle\\' => 31,
|
||||
),
|
||||
'C' =>
|
||||
array (
|
||||
'Composer\\CaBundle\\' => 18,
|
||||
'Clue\\StreamFilter\\' => 18,
|
||||
),
|
||||
'A' =>
|
||||
array (
|
||||
'Alcaeus\\MongoDbAdapter\\' => 23,
|
||||
@@ -125,6 +156,10 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/vimeo/vimeo-api/src/Vimeo',
|
||||
),
|
||||
'Twig\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/twig/twig/src',
|
||||
),
|
||||
'Tests\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/tests',
|
||||
@@ -145,6 +180,10 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||
),
|
||||
'Symfony\\Polyfill\\Apcu\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-apcu',
|
||||
),
|
||||
'Symfony\\Component\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/symfony/src/Symfony/Component',
|
||||
@@ -169,10 +208,6 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/symfony/src/Symfony/Bridge/Twig',
|
||||
),
|
||||
'Symfony\\Bridge\\Swiftmailer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/symfony/src/Symfony/Bridge/Swiftmailer',
|
||||
),
|
||||
'Symfony\\Bridge\\ProxyManager\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/symfony/src/Symfony/Bridge/ProxyManager',
|
||||
@@ -205,6 +240,10 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
|
||||
),
|
||||
'Psr\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||
),
|
||||
'Psr\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/cache/src',
|
||||
@@ -237,6 +276,57 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/incenteev/composer-parameter-handler',
|
||||
),
|
||||
'Http\\Promise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/promise/src',
|
||||
),
|
||||
'Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/message-factory/src',
|
||||
1 => __DIR__ . '/..' . '/php-http/message/src',
|
||||
),
|
||||
'Http\\HttplugBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/httplug-bundle',
|
||||
),
|
||||
'Http\\Discovery\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/discovery/src',
|
||||
),
|
||||
'Http\\Client\\Common\\Plugin\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/stopwatch-plugin/src',
|
||||
1 => __DIR__ . '/..' . '/php-http/logger-plugin/src',
|
||||
2 => __DIR__ . '/..' . '/php-http/cache-plugin/src',
|
||||
),
|
||||
'Http\\Client\\Common\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/client-common/src',
|
||||
),
|
||||
'Http\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/httplug/src',
|
||||
),
|
||||
'Http\\Adapter\\Guzzle6\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/php-http/guzzle6-adapter/src',
|
||||
),
|
||||
'HWI\\Bundle\\OAuthBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/hwi/oauth-bundle',
|
||||
),
|
||||
'GuzzleHttp\\Psr7\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
||||
),
|
||||
'GuzzleHttp\\Promise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
|
||||
),
|
||||
'GuzzleHttp\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
|
||||
),
|
||||
'FOS\\UserBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/friendsofsymfony/user-bundle',
|
||||
@@ -269,6 +359,10 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/migrations/lib/Doctrine/DBAL/Migrations',
|
||||
),
|
||||
'Doctrine\\Common\\Inflector\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Common/Inflector',
|
||||
),
|
||||
'Doctrine\\Common\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache',
|
||||
@@ -297,6 +391,14 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/doctrine-bundle',
|
||||
),
|
||||
'Composer\\CaBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/composer/ca-bundle/src',
|
||||
),
|
||||
'Clue\\StreamFilter\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/clue/stream-filter/src',
|
||||
),
|
||||
'Alcaeus\\MongoDbAdapter\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/alcaeus/mongo-php-adapter/lib/Alcaeus/MongoDbAdapter',
|
||||
@@ -389,10 +491,6 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/lib',
|
||||
),
|
||||
'Doctrine\\Common\\Inflector\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/inflector/lib',
|
||||
),
|
||||
'Doctrine\\Common\\Collections\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/collections/lib',
|
||||
@@ -431,6 +529,7 @@ class ComposerStaticInit1b32d02bd8fea2d524bf79b36ada5830
|
||||
'Locale' => __DIR__ . '/..' . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/Locale.php',
|
||||
'NumberFormatter' => __DIR__ . '/..' . '/symfony/symfony/src/Symfony/Component/Intl/Resources/stubs/NumberFormatter.php',
|
||||
'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
|
||||
'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
|
||||
'SqlFormatter' => __DIR__ . '/..' . '/jdorn/sql-formatter/lib/SqlFormatter.php',
|
||||
'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
|
||||
);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2016 Composer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,85 @@
|
||||
composer/ca-bundle
|
||||
==================
|
||||
|
||||
Small utility library that lets you find a path to the system CA bundle,
|
||||
and includes a fallback to the Mozilla CA bundle.
|
||||
|
||||
Originally written as part of [composer/composer](https://github.com/composer/composer),
|
||||
now extracted and made available as a stand-alone library.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install the latest version with:
|
||||
|
||||
```bash
|
||||
$ composer require composer/ca-bundle
|
||||
```
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* PHP 5.3.2 is required but using the latest version of PHP is highly recommended.
|
||||
|
||||
|
||||
Basic usage
|
||||
-----------
|
||||
|
||||
# `Composer\CaBundle\CaBundle`
|
||||
|
||||
- `CaBundle::getSystemCaRootBundlePath()`: Returns the system CA bundle path, or a path to the bundled one as fallback
|
||||
- `CaBundle::getBundledCaBundlePath()`: Returns the path to the bundled CA file
|
||||
- `CaBundle::validateCaFile($filename)`: Validates a CA file using opensl_x509_parse only if it is safe to use
|
||||
- `CaBundle::isOpensslParseSafe()`: Test if it is safe to use the PHP function openssl_x509_parse()
|
||||
- `CaBundle::reset()`: Resets the static caches
|
||||
|
||||
|
||||
## To use with curl
|
||||
|
||||
```php
|
||||
$curl = curl_init("https://example.org/");
|
||||
|
||||
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
|
||||
if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile)))) {
|
||||
curl_setopt($curl, CURLOPT_CAPATH, $caPathOrFile);
|
||||
} else {
|
||||
curl_setopt($curl, CURLOPT_CAINFO, $caPathOrFile);
|
||||
}
|
||||
|
||||
$result = curl_exec($curl);
|
||||
```
|
||||
|
||||
## To use with php streams
|
||||
|
||||
```php
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "GET"
|
||||
)
|
||||
);
|
||||
|
||||
$caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
|
||||
if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile)))) {
|
||||
$opts['ssl']['capath'] = $caPathOrFile;
|
||||
} else {
|
||||
$opts['ssl']['cafile'] = $caPathOrFile;
|
||||
}
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
$result = file_get_contents('https://example.com', false, $context);
|
||||
```
|
||||
|
||||
## To use with Guzzle
|
||||
|
||||
```php
|
||||
$client = new \GuzzleHttp\Client([
|
||||
\GuzzleHttp\RequestOptions::VERIFY => \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath()
|
||||
]);
|
||||
```
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
composer/ca-bundle is licensed under the MIT License, see the LICENSE file for details.
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "composer/ca-bundle",
|
||||
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"cabundle",
|
||||
"cacert",
|
||||
"certificate",
|
||||
"ssl",
|
||||
"tls"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "http://seld.be"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"irc": "irc://irc.freenode.org/composer",
|
||||
"issues": "https://github.com/composer/ca-bundle/issues"
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"php": "^5.3.2 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35",
|
||||
"psr/log": "^1.0",
|
||||
"symfony/process": "^2.5 || ^3.0 || ^4.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Composer\\CaBundle\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Composer\\CaBundle\\": "tests"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "5.3.9"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of composer/ca-bundle.
|
||||
*
|
||||
* (c) Composer <https://github.com/composer>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\CaBundle;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Process\PhpProcess;
|
||||
|
||||
/**
|
||||
* @author Chris Smith <chris@cs278.org>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class CaBundle
|
||||
{
|
||||
private static $caPath;
|
||||
private static $caFileValidity = array();
|
||||
private static $useOpensslParse;
|
||||
|
||||
/**
|
||||
* Returns the system CA bundle path, or a path to the bundled one
|
||||
*
|
||||
* This method was adapted from Sslurp.
|
||||
* https://github.com/EvanDotPro/Sslurp
|
||||
*
|
||||
* (c) Evan Coury <me@evancoury.com>
|
||||
*
|
||||
* For the full copyright and license information, please see below:
|
||||
*
|
||||
* Copyright (c) 2013, Evan Coury
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
|
||||
* @return string path to a CA bundle file or directory
|
||||
*/
|
||||
public static function getSystemCaRootBundlePath(LoggerInterface $logger = null)
|
||||
{
|
||||
if (self::$caPath !== null) {
|
||||
return self::$caPath;
|
||||
}
|
||||
|
||||
// If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
|
||||
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
|
||||
$envCertFile = getenv('SSL_CERT_FILE');
|
||||
if ($envCertFile && is_readable($envCertFile) && static::validateCaFile($envCertFile, $logger)) {
|
||||
return self::$caPath = $envCertFile;
|
||||
}
|
||||
|
||||
// If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
|
||||
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
|
||||
$envCertDir = getenv('SSL_CERT_DIR');
|
||||
if ($envCertDir && is_dir($envCertDir) && is_readable($envCertDir)) {
|
||||
return self::$caPath = $envCertDir;
|
||||
}
|
||||
|
||||
$configured = ini_get('openssl.cafile');
|
||||
if ($configured && strlen($configured) > 0 && is_readable($configured) && static::validateCaFile($configured, $logger)) {
|
||||
return self::$caPath = $configured;
|
||||
}
|
||||
|
||||
$configured = ini_get('openssl.capath');
|
||||
if ($configured && is_dir($configured) && is_readable($configured)) {
|
||||
return self::$caPath = $configured;
|
||||
}
|
||||
|
||||
$caBundlePaths = array(
|
||||
'/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
|
||||
'/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
|
||||
'/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
|
||||
'/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package)
|
||||
'/usr/ssl/certs/ca-bundle.crt', // Cygwin
|
||||
'/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
|
||||
'/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
|
||||
'/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
|
||||
'/etc/ssl/cert.pem', // OpenBSD
|
||||
'/usr/local/etc/ssl/cert.pem', // FreeBSD 10.x
|
||||
'/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
|
||||
);
|
||||
|
||||
foreach ($caBundlePaths as $caBundle) {
|
||||
if (@is_readable($caBundle) && static::validateCaFile($caBundle, $logger)) {
|
||||
return self::$caPath = $caBundle;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($caBundlePaths as $caBundle) {
|
||||
$caBundle = dirname($caBundle);
|
||||
if (@is_dir($caBundle) && glob($caBundle.'/*')) {
|
||||
return self::$caPath = $caBundle;
|
||||
}
|
||||
}
|
||||
|
||||
return self::$caPath = static::getBundledCaBundlePath(); // Bundled CA file, last resort
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the bundled CA file
|
||||
*
|
||||
* In case you don't want to trust the user or the system, you can use this directly
|
||||
*
|
||||
* @return string path to a CA bundle file
|
||||
*/
|
||||
public static function getBundledCaBundlePath()
|
||||
{
|
||||
$caBundleFile = __DIR__.'/../res/cacert.pem';
|
||||
|
||||
// cURL does not understand 'phar://' paths
|
||||
// see https://github.com/composer/ca-bundle/issues/10
|
||||
if (0 === strpos($caBundleFile, 'phar://')) {
|
||||
file_put_contents(
|
||||
$tempCaBundleFile = tempnam(sys_get_temp_dir(), 'openssl-ca-bundle-'),
|
||||
file_get_contents($caBundleFile)
|
||||
);
|
||||
|
||||
register_shutdown_function(function() use ($tempCaBundleFile) {
|
||||
@unlink($tempCaBundleFile);
|
||||
});
|
||||
|
||||
$caBundleFile = $tempCaBundleFile;
|
||||
}
|
||||
|
||||
return $caBundleFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a CA file using opensl_x509_parse only if it is safe to use
|
||||
*
|
||||
* @param string $filename
|
||||
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateCaFile($filename, LoggerInterface $logger = null)
|
||||
{
|
||||
static $warned = false;
|
||||
|
||||
if (isset(self::$caFileValidity[$filename])) {
|
||||
return self::$caFileValidity[$filename];
|
||||
}
|
||||
|
||||
$contents = file_get_contents($filename);
|
||||
|
||||
// assume the CA is valid if php is vulnerable to
|
||||
// https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
|
||||
if (!static::isOpensslParseSafe()) {
|
||||
if (!$warned && $logger) {
|
||||
$logger->warning(sprintf(
|
||||
'Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.',
|
||||
PHP_VERSION
|
||||
));
|
||||
$warned = true;
|
||||
}
|
||||
|
||||
$isValid = !empty($contents);
|
||||
} else {
|
||||
$isValid = (bool) openssl_x509_parse($contents);
|
||||
}
|
||||
|
||||
if ($logger) {
|
||||
$logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid'));
|
||||
}
|
||||
|
||||
return self::$caFileValidity[$filename] = $isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if it is safe to use the PHP function openssl_x509_parse().
|
||||
*
|
||||
* This checks if OpenSSL extensions is vulnerable to remote code execution
|
||||
* via the exploit documented as CVE-2013-6420.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isOpensslParseSafe()
|
||||
{
|
||||
if (null !== self::$useOpensslParse) {
|
||||
return self::$useOpensslParse;
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 50600) {
|
||||
return self::$useOpensslParse = true;
|
||||
}
|
||||
|
||||
// Vulnerable:
|
||||
// PHP 5.3.0 - PHP 5.3.27
|
||||
// PHP 5.4.0 - PHP 5.4.22
|
||||
// PHP 5.5.0 - PHP 5.5.6
|
||||
if (
|
||||
(PHP_VERSION_ID < 50400 && PHP_VERSION_ID >= 50328)
|
||||
|| (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50423)
|
||||
|| (PHP_VERSION_ID < 50600 && PHP_VERSION_ID >= 50507)
|
||||
) {
|
||||
// This version of PHP has the fix for CVE-2013-6420 applied.
|
||||
return self::$useOpensslParse = true;
|
||||
}
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
// Windows is probably insecure in this case.
|
||||
return self::$useOpensslParse = false;
|
||||
}
|
||||
|
||||
$compareDistroVersionPrefix = function ($prefix, $fixedVersion) {
|
||||
$regex = '{^'.preg_quote($prefix).'([0-9]+)$}';
|
||||
|
||||
if (preg_match($regex, PHP_VERSION, $m)) {
|
||||
return ((int) $m[1]) >= $fixedVersion;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Hard coded list of PHP distributions with the fix backported.
|
||||
if (
|
||||
$compareDistroVersionPrefix('5.3.3-7+squeeze', 18) // Debian 6 (Squeeze)
|
||||
|| $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) // Debian 7 (Wheezy)
|
||||
|| $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9) // Ubuntu 12.04 (Precise)
|
||||
) {
|
||||
return self::$useOpensslParse = true;
|
||||
}
|
||||
|
||||
// Symfony Process component is missing so we assume it is unsafe at this point
|
||||
if (!class_exists('Symfony\Component\Process\PhpProcess')) {
|
||||
return self::$useOpensslParse = false;
|
||||
}
|
||||
|
||||
// This is where things get crazy, because distros backport security
|
||||
// fixes the chances are on NIX systems the fix has been applied but
|
||||
// it's not possible to verify that from the PHP version.
|
||||
//
|
||||
// To verify exec a new PHP process and run the issue testcase with
|
||||
// known safe input that replicates the bug.
|
||||
|
||||
// Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415
|
||||
// changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593
|
||||
$cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K';
|
||||
$script = <<<'EOT'
|
||||
|
||||
error_reporting(-1);
|
||||
$info = openssl_x509_parse(base64_decode('%s'));
|
||||
var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']);
|
||||
|
||||
EOT;
|
||||
$script = '<'."?php\n".sprintf($script, $cert);
|
||||
|
||||
try {
|
||||
$process = new PhpProcess($script);
|
||||
$process->mustRun();
|
||||
} catch (\Exception $e) {
|
||||
// In the case of any exceptions just accept it is not possible to
|
||||
// determine the safety of openssl_x509_parse and bail out.
|
||||
return self::$useOpensslParse = false;
|
||||
}
|
||||
|
||||
$output = preg_split('{\r?\n}', trim($process->getOutput()));
|
||||
$errorOutput = trim($process->getErrorOutput());
|
||||
|
||||
if (
|
||||
count($output) === 3
|
||||
&& $output[0] === sprintf('string(%d) "%s"', strlen(PHP_VERSION), PHP_VERSION)
|
||||
&& $output[1] === 'string(27) "stefan.esser@sektioneins.de"'
|
||||
&& $output[2] === 'int(-1)'
|
||||
&& preg_match('{openssl_x509_parse\(\): illegal (?:ASN1 data type for|length in) timestamp in - on line \d+}', $errorOutput)
|
||||
) {
|
||||
// This PHP has the fix backported probably by a distro security team.
|
||||
return self::$useOpensslParse = true;
|
||||
}
|
||||
|
||||
return self::$useOpensslParse = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the static caches
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
self::$caFileValidity = array();
|
||||
self::$caPath = null;
|
||||
self::$useOpensslParse = null;
|
||||
}
|
||||
}
|
||||
+4344
-3105
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,20 @@
|
||||
## Changelog
|
||||
|
||||
### 1.5.0
|
||||
|
||||
This release increments the minimum supported PHP version to 7.1.0.
|
||||
|
||||
Also, HHVM official support has been dropped.
|
||||
|
||||
Some noticeable performance improvements to annotation autoloading
|
||||
have been applied, making failed annotation autoloading less heavy
|
||||
on the filesystem access.
|
||||
|
||||
- [133: Add @throws annotation in AnnotationReader#__construct()](https://github.com/doctrine/annotations/issues/133) thanks to @SenseException
|
||||
- [134: Require PHP 7.1, drop HHVM support](https://github.com/doctrine/annotations/issues/134) thanks to @lcobucci
|
||||
- [135: Prevent the same loader from being registered twice](https://github.com/doctrine/annotations/issues/135) thanks to @jrjohnson
|
||||
- [137: #135 optimise multiple class load attempts in AnnotationRegistry](https://github.com/doctrine/annotations/issues/137) thanks to @Ocramius
|
||||
|
||||
|
||||
### 1.4.0
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0",
|
||||
"php": "^7.1",
|
||||
"doctrine/lexer": "1.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/cache": "1.*",
|
||||
"phpunit/phpunit": "^5.7"
|
||||
"phpunit/phpunit": "^6.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" }
|
||||
@@ -28,7 +28,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4.x-dev"
|
||||
"dev-master": "1.6.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -100,6 +100,8 @@ class AnnotationReader implements Reader
|
||||
'package_version' => true,
|
||||
// PlantUML
|
||||
'startuml' => true, 'enduml' => true,
|
||||
// Symfony 3.3 Cache Adapter
|
||||
'experimental' => true
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -172,6 +174,8 @@ class AnnotationReader implements Reader
|
||||
* Initializes a new AnnotationReader.
|
||||
*
|
||||
* @param DocParser $parser
|
||||
*
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function __construct(DocParser $parser = null)
|
||||
{
|
||||
|
||||
+64
-41
@@ -19,9 +19,6 @@
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
/**
|
||||
* AnnotationRegistry.
|
||||
*/
|
||||
final class AnnotationRegistry
|
||||
{
|
||||
/**
|
||||
@@ -32,34 +29,39 @@ final class AnnotationRegistry
|
||||
*
|
||||
* This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own.
|
||||
*
|
||||
* @var array
|
||||
* @var string[][]|string[]|null[]
|
||||
*/
|
||||
static private $autoloadNamespaces = array();
|
||||
static private $autoloadNamespaces = [];
|
||||
|
||||
/**
|
||||
* A map of autoloader callables.
|
||||
*
|
||||
* @var array
|
||||
* @var callable[]
|
||||
*/
|
||||
static private $loaders = array();
|
||||
static private $loaders = [];
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* An array of classes which cannot be found
|
||||
*
|
||||
* @var null[] indexed by class name
|
||||
*/
|
||||
static public function reset()
|
||||
static private $failedToAutoload = [];
|
||||
|
||||
public static function reset() : void
|
||||
{
|
||||
self::$autoloadNamespaces = array();
|
||||
self::$loaders = array();
|
||||
self::$autoloadNamespaces = [];
|
||||
self::$loaders = [];
|
||||
self::$failedToAutoload = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers file.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return void
|
||||
* @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
|
||||
* autoloading should be deferred to the globally registered autoloader by then. For now,
|
||||
* use @example AnnotationRegistry::registerLoader('class_exists')
|
||||
*/
|
||||
static public function registerFile($file)
|
||||
public static function registerFile(string $file) : void
|
||||
{
|
||||
require_once $file;
|
||||
}
|
||||
@@ -72,9 +74,11 @@ final class AnnotationRegistry
|
||||
* @param string $namespace
|
||||
* @param string|array|null $dirs
|
||||
*
|
||||
* @return void
|
||||
* @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
|
||||
* autoloading should be deferred to the globally registered autoloader by then. For now,
|
||||
* use @example AnnotationRegistry::registerLoader('class_exists')
|
||||
*/
|
||||
static public function registerAutoloadNamespace($namespace, $dirs = null)
|
||||
public static function registerAutoloadNamespace(string $namespace, $dirs = null) : void
|
||||
{
|
||||
self::$autoloadNamespaces[$namespace] = $dirs;
|
||||
}
|
||||
@@ -84,13 +88,15 @@ final class AnnotationRegistry
|
||||
*
|
||||
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
|
||||
*
|
||||
* @param array $namespaces
|
||||
* @param string[][]|string[]|null[] $namespaces indexed by namespace name
|
||||
*
|
||||
* @return void
|
||||
* @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
|
||||
* autoloading should be deferred to the globally registered autoloader by then. For now,
|
||||
* use @example AnnotationRegistry::registerLoader('class_exists')
|
||||
*/
|
||||
static public function registerAutoloadNamespaces(array $namespaces)
|
||||
public static function registerAutoloadNamespaces(array $namespaces) : void
|
||||
{
|
||||
self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
|
||||
self::$autoloadNamespaces = \array_merge(self::$autoloadNamespaces, $namespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,41 +105,55 @@ final class AnnotationRegistry
|
||||
* NOTE: These class loaders HAVE to be silent when a class was not found!
|
||||
* IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
|
||||
*
|
||||
* @param callable $callable
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
|
||||
* autoloading should be deferred to the globally registered autoloader by then. For now,
|
||||
* use @example AnnotationRegistry::registerLoader('class_exists')
|
||||
*/
|
||||
static public function registerLoader($callable)
|
||||
public static function registerLoader(callable $callable) : void
|
||||
{
|
||||
if (!is_callable($callable)) {
|
||||
throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader().");
|
||||
// Reset our static cache now that we have a new loader to work with
|
||||
self::$failedToAutoload = [];
|
||||
self::$loaders[] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an autoloading callable for annotations, if it is not already registered
|
||||
*
|
||||
* @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
|
||||
*/
|
||||
public static function registerUniqueLoader(callable $callable) : void
|
||||
{
|
||||
if ( ! in_array($callable, self::$loaders, true) ) {
|
||||
self::registerLoader($callable);
|
||||
}
|
||||
self::$loaders[] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoloads an annotation class silently.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
static public function loadAnnotationClass($class)
|
||||
public static function loadAnnotationClass(string $class) : bool
|
||||
{
|
||||
if (\class_exists($class, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (\array_key_exists($class, self::$failedToAutoload)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (self::$autoloadNamespaces AS $namespace => $dirs) {
|
||||
if (strpos($class, $namespace) === 0) {
|
||||
$file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
|
||||
if (\strpos($class, $namespace) === 0) {
|
||||
$file = \str_replace('\\', \DIRECTORY_SEPARATOR, $class) . '.php';
|
||||
|
||||
if ($dirs === null) {
|
||||
if ($path = stream_resolve_include_path($file)) {
|
||||
require $path;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
foreach((array)$dirs AS $dir) {
|
||||
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
|
||||
require $dir . DIRECTORY_SEPARATOR . $file;
|
||||
foreach((array) $dirs AS $dir) {
|
||||
if (is_file($dir . \DIRECTORY_SEPARATOR . $file)) {
|
||||
require $dir . \DIRECTORY_SEPARATOR . $file;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -142,10 +162,13 @@ final class AnnotationRegistry
|
||||
}
|
||||
|
||||
foreach (self::$loaders AS $loader) {
|
||||
if (call_user_func($loader, $class) === true) {
|
||||
if ($loader($class) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
self::$failedToAutoload[$class] = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ parameters:
|
||||
- %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php
|
||||
excludes_analyse:
|
||||
- %currentWorkingDirectory%/tests/*/Fixtures/*
|
||||
- %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/ReservedKeywordsClasses.php
|
||||
- %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php
|
||||
- %currentWorkingDirectory%/tests/Doctrine/Tests/DoctrineTestCase.php
|
||||
polluteScopeWithLoopInitialAssignments: true
|
||||
ignoreErrors:
|
||||
- '#Class Doctrine_Tests_Common_Annotations_Fixtures_ClassNoNamespaceNoComment not found#'
|
||||
|
||||
+4
-9
@@ -1,14 +1,9 @@
|
||||
# Doctrine Cache
|
||||
|
||||
Master: [](http://travis-ci.org/doctrine/cache) [](https://coveralls.io/r/doctrine/cache?branch=master)
|
||||
[](http://travis-ci.org/doctrine/cache)
|
||||
[](https://scrutinizer-ci.com/g/doctrine/cache/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/doctrine/cache/?branch=master)
|
||||
|
||||
[](https://packagist.org/packages/doctrine/cache) [](https://packagist.org/packages/doctrine/cache)
|
||||
|
||||
Cache component extracted from the Doctrine Common project.
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.2
|
||||
|
||||
* Added support for MongoDB as Cache Provider
|
||||
* Fix namespace version reset
|
||||
Cache component extracted from the Doctrine Common project. [Documentation](http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/caching.html)
|
||||
|
||||
@@ -13,12 +13,16 @@
|
||||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": "~5.5|~7.0"
|
||||
"php": "~7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8|~5.0",
|
||||
"satooshi/php-coveralls": "~0.6",
|
||||
"predis/predis": "~1.0"
|
||||
"alcaeus/mongo-php-adapter": "^1.1",
|
||||
"mongodb/mongodb": "^1.1",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"predis/predis": "~1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/common": ">2.2,<2.4"
|
||||
@@ -31,7 +35,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.6.x-dev"
|
||||
"dev-master": "1.7.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -107,12 +107,12 @@ class ApcCache extends CacheProvider
|
||||
$info['start_time'] = isset($info['start_time']) ? $info['start_time'] : $info['stime'];
|
||||
}
|
||||
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => $info['num_hits'],
|
||||
Cache::STATS_MISSES => $info['num_misses'],
|
||||
Cache::STATS_UPTIME => $info['start_time'],
|
||||
Cache::STATS_MEMORY_USAGE => $info['mem_size'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+12
-2
@@ -61,6 +61,16 @@ class ApcuCache extends CacheProvider
|
||||
return apcu_delete($id) || ! apcu_exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$result = apcu_delete($keys);
|
||||
|
||||
return false !== $result && count($result) !== count($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -95,12 +105,12 @@ class ApcuCache extends CacheProvider
|
||||
$info = apcu_cache_info(true);
|
||||
$sma = apcu_sma_info();
|
||||
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => $info['num_hits'],
|
||||
Cache::STATS_MISSES => $info['num_misses'],
|
||||
Cache::STATS_UPTIME => $info['start_time'],
|
||||
Cache::STATS_MEMORY_USAGE => $info['mem_size'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+41
-12
@@ -28,8 +28,9 @@ namespace Doctrine\Common\Cache;
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
* @author Benoit Burnichon <bburnichon@gmail.com>
|
||||
*/
|
||||
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiGetCache, MultiPutCache
|
||||
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiOperationCache
|
||||
{
|
||||
const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
|
||||
|
||||
@@ -84,13 +85,13 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
||||
public function fetchMultiple(array $keys)
|
||||
{
|
||||
if (empty($keys)) {
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
// note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys
|
||||
$namespacedKeys = array_combine($keys, array_map(array($this, 'getNamespacedId'), $keys));
|
||||
$namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys));
|
||||
$items = $this->doFetchMultiple($namespacedKeys);
|
||||
$foundItems = array();
|
||||
$foundItems = [];
|
||||
|
||||
// no internal array function supports this sort of mapping: needs to be iterative
|
||||
// this filters and combines keys in one pass
|
||||
@@ -108,7 +109,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
||||
*/
|
||||
public function saveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
$namespacedKeysAndValues = array();
|
||||
$namespacedKeysAndValues = [];
|
||||
foreach ($keysAndValues as $key => $value) {
|
||||
$namespacedKeysAndValues[$this->getNamespacedId($key)] = $value;
|
||||
}
|
||||
@@ -132,6 +133,14 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
||||
return $this->doSave($this->getNamespacedId($id), $data, $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $keys)
|
||||
{
|
||||
return $this->doDeleteMultiple(array_map(array($this, 'getNamespacedId'), $keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -180,7 +189,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
||||
*
|
||||
* @return string The namespaced id.
|
||||
*/
|
||||
private function getNamespacedId($id)
|
||||
private function getNamespacedId(string $id) : string
|
||||
{
|
||||
$namespaceVersion = $this->getNamespaceVersion();
|
||||
|
||||
@@ -192,7 +201,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getNamespaceCacheKey()
|
||||
private function getNamespaceCacheKey() : string
|
||||
{
|
||||
return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace);
|
||||
}
|
||||
@@ -202,14 +211,14 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
private function getNamespaceVersion()
|
||||
private function getNamespaceVersion() : int
|
||||
{
|
||||
if (null !== $this->namespaceVersion) {
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$this->namespaceVersion = $this->doFetch($namespaceCacheKey) ?: 1;
|
||||
$namespaceCacheKey = $this->getNamespaceCacheKey();
|
||||
$this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1;
|
||||
|
||||
return $this->namespaceVersion;
|
||||
}
|
||||
@@ -222,7 +231,7 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
||||
*/
|
||||
protected function doFetchMultiple(array $keys)
|
||||
{
|
||||
$returnValues = array();
|
||||
$returnValues = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (false !== ($item = $this->doFetch($key)) || $this->doContains($key)) {
|
||||
@@ -285,6 +294,26 @@ abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, M
|
||||
*/
|
||||
abstract protected function doSave($id, $data, $lifeTime = 0);
|
||||
|
||||
/**
|
||||
* Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it.
|
||||
*
|
||||
* @param array $keys Array of keys to delete from cache
|
||||
*
|
||||
* @return bool TRUE if the operation was successful, FALSE if it wasn't
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$success = true;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (! $this->doDelete($key)) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a cache entry.
|
||||
*
|
||||
|
||||
+62
-4
@@ -29,16 +29,18 @@ class ChainCache extends CacheProvider
|
||||
/**
|
||||
* @var CacheProvider[]
|
||||
*/
|
||||
private $cacheProviders = array();
|
||||
private $cacheProviders = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param CacheProvider[] $cacheProviders
|
||||
*/
|
||||
public function __construct($cacheProviders = array())
|
||||
public function __construct($cacheProviders = [])
|
||||
{
|
||||
$this->cacheProviders = $cacheProviders;
|
||||
$this->cacheProviders = $cacheProviders instanceof \Traversable
|
||||
? iterator_to_array($cacheProviders, false)
|
||||
: array_values($cacheProviders);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,6 +76,34 @@ class ChainCache extends CacheProvider
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetchMultiple(array $keys)
|
||||
{
|
||||
/* @var $traversedProviders CacheProvider[] */
|
||||
$traversedProviders = [];
|
||||
$keysCount = count($keys);
|
||||
$fetchedValues = [];
|
||||
|
||||
foreach ($this->cacheProviders as $key => $cacheProvider) {
|
||||
$fetchedValues = $cacheProvider->doFetchMultiple($keys);
|
||||
|
||||
// We populate all the previous cache layers (that are assumed to be faster)
|
||||
if (count($fetchedValues) === $keysCount) {
|
||||
foreach ($traversedProviders as $previousCacheProvider) {
|
||||
$previousCacheProvider->doSaveMultiple($fetchedValues);
|
||||
}
|
||||
|
||||
return $fetchedValues;
|
||||
}
|
||||
|
||||
$traversedProviders[] = $cacheProvider;
|
||||
}
|
||||
|
||||
return $fetchedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -102,6 +132,20 @@ class ChainCache extends CacheProvider
|
||||
return $stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
|
||||
{
|
||||
$stored = true;
|
||||
|
||||
foreach ($this->cacheProviders as $cacheProvider) {
|
||||
$stored = $cacheProvider->doSaveMultiple($keysAndValues, $lifetime) && $stored;
|
||||
}
|
||||
|
||||
return $stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -116,6 +160,20 @@ class ChainCache extends CacheProvider
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
$deleted = true;
|
||||
|
||||
foreach ($this->cacheProviders as $cacheProvider) {
|
||||
$deleted = $cacheProvider->doDeleteMultiple($keys) && $deleted;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -136,7 +194,7 @@ class ChainCache extends CacheProvider
|
||||
protected function doGetStats()
|
||||
{
|
||||
// We return all the stats from all adapters
|
||||
$stats = array();
|
||||
$stats = [];
|
||||
|
||||
foreach ($this->cacheProviders as $cacheProvider) {
|
||||
$stats[] = $cacheProvider->doGetStats();
|
||||
|
||||
+2
-2
@@ -110,12 +110,12 @@ class CouchbaseCache extends CacheProvider
|
||||
$server = explode(":", $servers[0]);
|
||||
$key = $server[0] . ":" . "11210";
|
||||
$stats = $stats[$key];
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use MongoDB\BSON\Binary;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use MongoDB\Collection;
|
||||
use MongoDB\Database;
|
||||
use MongoDB\Driver\Exception\Exception;
|
||||
use MongoDB\Model\BSONDocument;
|
||||
|
||||
/**
|
||||
* MongoDB cache provider for ext-mongodb
|
||||
*
|
||||
* @internal Do not use - will be removed in 2.0. Use MongoDBCache instead
|
||||
*/
|
||||
class ExtMongoDBCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var Database
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* @var Collection
|
||||
*/
|
||||
private $collection;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $expirationIndexCreated = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* This provider will default to the write concern and read preference
|
||||
* options set on the Database instance (or inherited from MongoDB or
|
||||
* Client). Using an unacknowledged write concern (< 1) may make the return
|
||||
* values of delete() and save() unreliable. Reading from secondaries may
|
||||
* make contain() and fetch() unreliable.
|
||||
*
|
||||
* @see http://www.php.net/manual/en/mongo.readpreferences.php
|
||||
* @see http://www.php.net/manual/en/mongo.writeconcerns.php
|
||||
* @param Collection $collection
|
||||
*/
|
||||
public function __construct(Collection $collection)
|
||||
{
|
||||
// Ensure there is no typemap set - we want to use our own
|
||||
$this->collection = $collection->withOptions(['typeMap' => null]);
|
||||
$this->database = new Database($collection->getManager(), $collection->getDatabaseName());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]);
|
||||
|
||||
if ($document === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isExpired($document)) {
|
||||
$this->createExpirationIndex();
|
||||
$this->doDelete($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return unserialize($document[MongoDBCache::DATA_FIELD]->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
$document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]);
|
||||
|
||||
if ($document === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isExpired($document)) {
|
||||
$this->createExpirationIndex();
|
||||
$this->doDelete($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
try {
|
||||
$this->collection->updateOne(
|
||||
['_id' => $id],
|
||||
['$set' => [
|
||||
MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new UTCDateTime((time() + $lifeTime) * 1000): null),
|
||||
MongoDBCache::DATA_FIELD => new Binary(serialize($data), Binary::TYPE_GENERIC),
|
||||
]],
|
||||
['upsert' => true]
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
try {
|
||||
$this->collection->deleteOne(['_id' => $id]);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
try {
|
||||
// Use remove() in lieu of drop() to maintain any collection indexes
|
||||
$this->collection->deleteMany([]);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$uptime = null;
|
||||
$memoryUsage = null;
|
||||
|
||||
try {
|
||||
$serverStatus = $this->database->command([
|
||||
'serverStatus' => 1,
|
||||
'locks' => 0,
|
||||
'metrics' => 0,
|
||||
'recordStats' => 0,
|
||||
'repl' => 0,
|
||||
])->toArray()[0];
|
||||
$uptime = $serverStatus['uptime'] ?? null;
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
$collStats = $this->database->command(['collStats' => $this->collection->getCollectionName()])->toArray()[0];
|
||||
$memoryUsage = $collStats['size'] ?? null;
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
return [
|
||||
Cache::STATS_HITS => null,
|
||||
Cache::STATS_MISSES => null,
|
||||
Cache::STATS_UPTIME => $uptime,
|
||||
Cache::STATS_MEMORY_USAGE => $memoryUsage,
|
||||
Cache::STATS_MEMORY_AVAILABLE => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the document is expired.
|
||||
*
|
||||
* @param BSONDocument $document
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isExpired(BSONDocument $document): bool
|
||||
{
|
||||
return isset($document[MongoDBCache::EXPIRATION_FIELD]) &&
|
||||
$document[MongoDBCache::EXPIRATION_FIELD] instanceof UTCDateTime &&
|
||||
$document[MongoDBCache::EXPIRATION_FIELD]->toDateTime() < new \DateTime();
|
||||
}
|
||||
|
||||
private function createExpirationIndex(): void
|
||||
{
|
||||
if ($this->expirationIndexCreated) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]);
|
||||
}
|
||||
}
|
||||
+7
-6
@@ -202,13 +202,13 @@ abstract class FileCache extends CacheProvider
|
||||
|
||||
$free = disk_free_space($this->directory);
|
||||
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => null,
|
||||
Cache::STATS_MISSES => null,
|
||||
Cache::STATS_UPTIME => null,
|
||||
Cache::STATS_MEMORY_USAGE => $usage,
|
||||
Cache::STATS_MEMORY_AVAILABLE => $free,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,7 +217,7 @@ abstract class FileCache extends CacheProvider
|
||||
* @param string $path
|
||||
* @return bool TRUE on success or if path already exists, FALSE if path cannot be created.
|
||||
*/
|
||||
private function createPathIfNeeded($path)
|
||||
private function createPathIfNeeded(string $path) : bool
|
||||
{
|
||||
if ( ! is_dir($path)) {
|
||||
if (false === @mkdir($path, 0777 & (~$this->umask), true) && !is_dir($path)) {
|
||||
@@ -236,7 +236,7 @@ abstract class FileCache extends CacheProvider
|
||||
*
|
||||
* @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error.
|
||||
*/
|
||||
protected function writeFile($filename, $content)
|
||||
protected function writeFile(string $filename, string $content) : bool
|
||||
{
|
||||
$filepath = pathinfo($filename, PATHINFO_DIRNAME);
|
||||
|
||||
@@ -252,6 +252,7 @@ abstract class FileCache extends CacheProvider
|
||||
@chmod($tmpFile, 0666 & (~$this->umask));
|
||||
|
||||
if (file_put_contents($tmpFile, $content) !== false) {
|
||||
@chmod($tmpFile, 0666 & (~$this->umask));
|
||||
if (@rename($tmpFile, $filename)) {
|
||||
return true;
|
||||
}
|
||||
@@ -265,7 +266,7 @@ abstract class FileCache extends CacheProvider
|
||||
/**
|
||||
* @return \Iterator
|
||||
*/
|
||||
private function getIterator()
|
||||
private function getIterator() : \Iterator
|
||||
{
|
||||
return new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS),
|
||||
@@ -278,7 +279,7 @@ abstract class FileCache extends CacheProvider
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isFilenameEndingWithExtension($name)
|
||||
private function isFilenameEndingWithExtension(string $name) : bool
|
||||
{
|
||||
return '' === $this->extension
|
||||
|| strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength);
|
||||
|
||||
Vendored
+194
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use MongoBinData;
|
||||
use MongoCollection;
|
||||
use MongoCursorException;
|
||||
use MongoDate;
|
||||
|
||||
/**
|
||||
* MongoDB cache provider.
|
||||
*
|
||||
* @author Jeremy Mikola <jmikola@gmail.com>
|
||||
* @internal Do not use - will be removed in 2.0. Use MongoDBCache instead
|
||||
*/
|
||||
class LegacyMongoDBCache extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var MongoCollection
|
||||
*/
|
||||
private $collection;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $expirationIndexCreated = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* This provider will default to the write concern and read preference
|
||||
* options set on the MongoCollection instance (or inherited from MongoDB or
|
||||
* MongoClient). Using an unacknowledged write concern (< 1) may make the
|
||||
* return values of delete() and save() unreliable. Reading from secondaries
|
||||
* may make contain() and fetch() unreliable.
|
||||
*
|
||||
* @see http://www.php.net/manual/en/mongo.readpreferences.php
|
||||
* @see http://www.php.net/manual/en/mongo.writeconcerns.php
|
||||
* @param MongoCollection $collection
|
||||
*/
|
||||
public function __construct(MongoCollection $collection)
|
||||
{
|
||||
@trigger_error('Using the legacy MongoDB cache provider is deprecated and will be removed in 2.0', E_USER_DEPRECATED);
|
||||
$this->collection = $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]);
|
||||
|
||||
if ($document === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isExpired($document)) {
|
||||
$this->createExpirationIndex();
|
||||
$this->doDelete($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return unserialize($document[MongoDBCache::DATA_FIELD]->bin);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
$document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]);
|
||||
|
||||
if ($document === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isExpired($document)) {
|
||||
$this->createExpirationIndex();
|
||||
$this->doDelete($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
try {
|
||||
$result = $this->collection->update(
|
||||
['_id' => $id],
|
||||
['$set' => [
|
||||
MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null),
|
||||
MongoDBCache::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY),
|
||||
]],
|
||||
['upsert' => true, 'multiple' => false]
|
||||
);
|
||||
} catch (MongoCursorException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($result['ok'] ?? 1) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
$result = $this->collection->remove(['_id' => $id]);
|
||||
|
||||
return ($result['ok'] ?? 1) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
// Use remove() in lieu of drop() to maintain any collection indexes
|
||||
$result = $this->collection->remove();
|
||||
|
||||
return ($result['ok'] ?? 1) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$serverStatus = $this->collection->db->command([
|
||||
'serverStatus' => 1,
|
||||
'locks' => 0,
|
||||
'metrics' => 0,
|
||||
'recordStats' => 0,
|
||||
'repl' => 0,
|
||||
]);
|
||||
|
||||
$collStats = $this->collection->db->command(['collStats' => 1]);
|
||||
|
||||
return [
|
||||
Cache::STATS_HITS => null,
|
||||
Cache::STATS_MISSES => null,
|
||||
Cache::STATS_UPTIME => $serverStatus['uptime'] ?? null,
|
||||
Cache::STATS_MEMORY_USAGE => $collStats['size'] ?? null,
|
||||
Cache::STATS_MEMORY_AVAILABLE => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the document is expired.
|
||||
*
|
||||
* @param array $document
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isExpired(array $document) : bool
|
||||
{
|
||||
return isset($document[MongoDBCache::EXPIRATION_FIELD]) &&
|
||||
$document[MongoDBCache::EXPIRATION_FIELD] instanceof MongoDate &&
|
||||
$document[MongoDBCache::EXPIRATION_FIELD]->sec < time();
|
||||
}
|
||||
|
||||
|
||||
private function createExpirationIndex(): void
|
||||
{
|
||||
if ($this->expirationIndexCreated) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->expirationIndexCreated = true;
|
||||
$this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -115,12 +115,12 @@ class MemcacheCache extends CacheProvider
|
||||
protected function doGetStats()
|
||||
{
|
||||
$stats = $this->memcache->getStats();
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+14
-4
@@ -94,8 +94,9 @@ class MemcachedCache extends CacheProvider
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return false !== $this->memcached->get($id)
|
||||
|| $this->memcached->getResultCode() !== Memcached::RES_NOTFOUND;
|
||||
$this->memcached->get($id);
|
||||
|
||||
return $this->memcached->getResultCode() === Memcached::RES_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +110,15 @@ class MemcachedCache extends CacheProvider
|
||||
return $this->memcached->set($id, $data, (int) $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteMultiple(array $keys)
|
||||
{
|
||||
return $this->memcached->deleteMulti($keys)
|
||||
|| $this->memcached->getResultCode() === Memcached::RES_NOTFOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -135,12 +145,12 @@ class MemcachedCache extends CacheProvider
|
||||
$servers = $this->memcached->getServerList();
|
||||
$key = $servers[0]['host'] . ':' . $servers[0]['port'];
|
||||
$stats = $stats[$key];
|
||||
return array(
|
||||
return [
|
||||
Cache::STATS_HITS => $stats['get_hits'],
|
||||
Cache::STATS_MISSES => $stats['get_misses'],
|
||||
Cache::STATS_UPTIME => $stats['uptime'],
|
||||
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
|
||||
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+20
-85
@@ -19,10 +19,8 @@
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
use MongoBinData;
|
||||
use MongoCollection;
|
||||
use MongoCursorException;
|
||||
use MongoDate;
|
||||
use MongoDB\Collection;
|
||||
|
||||
/**
|
||||
* MongoDB cache provider.
|
||||
@@ -55,26 +53,33 @@ class MongoDBCache extends CacheProvider
|
||||
const EXPIRATION_FIELD = 'e';
|
||||
|
||||
/**
|
||||
* @var MongoCollection
|
||||
* @var CacheProvider
|
||||
*/
|
||||
private $collection;
|
||||
private $provider;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* This provider will default to the write concern and read preference
|
||||
* options set on the MongoCollection instance (or inherited from MongoDB or
|
||||
* options set on the collection instance (or inherited from MongoDB or
|
||||
* MongoClient). Using an unacknowledged write concern (< 1) may make the
|
||||
* return values of delete() and save() unreliable. Reading from secondaries
|
||||
* may make contain() and fetch() unreliable.
|
||||
*
|
||||
* @see http://www.php.net/manual/en/mongo.readpreferences.php
|
||||
* @see http://www.php.net/manual/en/mongo.writeconcerns.php
|
||||
* @param MongoCollection $collection
|
||||
* @param MongoCollection|Collection $collection
|
||||
*/
|
||||
public function __construct(MongoCollection $collection)
|
||||
public function __construct($collection)
|
||||
{
|
||||
$this->collection = $collection;
|
||||
if ($collection instanceof MongoCollection) {
|
||||
@trigger_error('Using a MongoCollection instance for creating a cache adapter is deprecated and will be removed in 2.0', E_USER_DEPRECATED);
|
||||
$this->provider = new LegacyMongoDBCache($collection);
|
||||
} elseif ($collection instanceof Collection) {
|
||||
$this->provider = new ExtMongoDBCache($collection);
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid collection given - expected a MongoCollection or MongoDB\Collection instance');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,18 +87,7 @@ class MongoDBCache extends CacheProvider
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$document = $this->collection->findOne(array('_id' => $id), array(self::DATA_FIELD, self::EXPIRATION_FIELD));
|
||||
|
||||
if ($document === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isExpired($document)) {
|
||||
$this->doDelete($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return unserialize($document[self::DATA_FIELD]->bin);
|
||||
return $this->provider->doFetch($id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,18 +95,7 @@ class MongoDBCache extends CacheProvider
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
$document = $this->collection->findOne(array('_id' => $id), array(self::EXPIRATION_FIELD));
|
||||
|
||||
if ($document === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isExpired($document)) {
|
||||
$this->doDelete($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return $this->provider->doContains($id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,20 +103,7 @@ class MongoDBCache extends CacheProvider
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
try {
|
||||
$result = $this->collection->update(
|
||||
array('_id' => $id),
|
||||
array('$set' => array(
|
||||
self::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null),
|
||||
self::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY),
|
||||
)),
|
||||
array('upsert' => true, 'multiple' => false)
|
||||
);
|
||||
} catch (MongoCursorException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset($result['ok']) ? $result['ok'] == 1 : true;
|
||||
return $this->provider->doSave($id, $data, $lifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,9 +111,7 @@ class MongoDBCache extends CacheProvider
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
$result = $this->collection->remove(array('_id' => $id));
|
||||
|
||||
return isset($result['ok']) ? $result['ok'] == 1 : true;
|
||||
return $this->provider->doDelete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,10 +119,7 @@ class MongoDBCache extends CacheProvider
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
// Use remove() in lieu of drop() to maintain any collection indexes
|
||||
$result = $this->collection->remove();
|
||||
|
||||
return isset($result['ok']) ? $result['ok'] == 1 : true;
|
||||
return $this->provider->doFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,36 +127,6 @@ class MongoDBCache extends CacheProvider
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
$serverStatus = $this->collection->db->command(array(
|
||||
'serverStatus' => 1,
|
||||
'locks' => 0,
|
||||
'metrics' => 0,
|
||||
'recordStats' => 0,
|
||||
'repl' => 0,
|
||||
));
|
||||
|
||||
$collStats = $this->collection->db->command(array('collStats' => 1));
|
||||
|
||||
return array(
|
||||
Cache::STATS_HITS => null,
|
||||
Cache::STATS_MISSES => null,
|
||||
Cache::STATS_UPTIME => (isset($serverStatus['uptime']) ? (int) $serverStatus['uptime'] : null),
|
||||
Cache::STATS_MEMORY_USAGE => (isset($collStats['size']) ? (int) $collStats['size'] : null),
|
||||
Cache::STATS_MEMORY_AVAILABLE => null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the document is expired.
|
||||
*
|
||||
* @param array $document
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isExpired(array $document)
|
||||
{
|
||||
return isset($document[self::EXPIRATION_FIELD]) &&
|
||||
$document[self::EXPIRATION_FIELD] instanceof MongoDate &&
|
||||
$document[self::EXPIRATION_FIELD]->sec < time();
|
||||
return $this->provider->doGetStats();
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache drivers that allows to put many items at once.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.7
|
||||
* @author Benoit Burnichon <bburnichon@gmail.com>
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
interface MultiDeleteCache
|
||||
{
|
||||
/**
|
||||
* Deletes several cache entries.
|
||||
*
|
||||
* @param string[] $keys Array of keys to delete from cache
|
||||
*
|
||||
* @return bool TRUE if the operation was successful, FALSE if it wasn't.
|
||||
*/
|
||||
function deleteMultiple(array $keys);
|
||||
}
|
||||
+2
@@ -25,6 +25,8 @@ namespace Doctrine\Common\Cache;
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.4
|
||||
* @author Asmir Mustafic <goetas@gmail.com>
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
interface MultiGetCache
|
||||
{
|
||||
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Interface for cache drivers that supports multiple items manipulation.
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.7
|
||||
* @author Luís Cobucci <lcobucci@gmail.com>
|
||||
*/
|
||||
interface MultiOperationCache extends MultiGetCache, MultiDeleteCache, MultiPutCache
|
||||
{
|
||||
}
|
||||
+2
@@ -25,6 +25,8 @@ namespace Doctrine\Common\Cache;
|
||||
* @link www.doctrine-project.org
|
||||
* @since 1.6
|
||||
* @author Daniel Gorgan <danut007ro@gmail.com>
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
interface MultiPutCache
|
||||
{
|
||||
|
||||
+29
-18
@@ -29,12 +29,22 @@ class PhpFileCache extends FileCache
|
||||
{
|
||||
const EXTENSION = '.doctrinecache.php';
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*
|
||||
* This is cached in a local static variable to avoid instantiating a closure each time we need an empty handler
|
||||
*/
|
||||
private static $emptyErrorHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($directory, $extension = self::EXTENSION, $umask = 0002)
|
||||
{
|
||||
parent::__construct($directory, $extension, $umask);
|
||||
|
||||
self::$emptyErrorHandler = function () {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,7 +54,7 @@ class PhpFileCache extends FileCache
|
||||
{
|
||||
$value = $this->includeFileForId($id);
|
||||
|
||||
if (! $value) {
|
||||
if ($value === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -62,7 +72,7 @@ class PhpFileCache extends FileCache
|
||||
{
|
||||
$value = $this->includeFileForId($id);
|
||||
|
||||
if (! $value) {
|
||||
if ($value === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -78,23 +88,20 @@ class PhpFileCache extends FileCache
|
||||
$lifeTime = time() + $lifeTime;
|
||||
}
|
||||
|
||||
if (is_object($data) && ! method_exists($data, '__set_state')) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Invalid argument given, PhpFileCache only allows objects that implement __set_state() " .
|
||||
"and fully support var_export(). You can use the FilesystemCache to save arbitrary object " .
|
||||
"graphs using serialize()/deserialize()."
|
||||
);
|
||||
}
|
||||
|
||||
$filename = $this->getFilename($id);
|
||||
|
||||
$value = array(
|
||||
$value = [
|
||||
'lifetime' => $lifeTime,
|
||||
'data' => $data
|
||||
);
|
||||
];
|
||||
|
||||
$value = var_export($value, true);
|
||||
$code = sprintf('<?php return %s;', $value);
|
||||
if (is_object($data) && method_exists($data, '__set_state')) {
|
||||
$value = var_export($value, true);
|
||||
$code = sprintf('<?php return %s;', $value);
|
||||
} else {
|
||||
$value = var_export(serialize($value), true);
|
||||
$code = sprintf('<?php return unserialize(%s);', $value);
|
||||
}
|
||||
|
||||
return $this->writeFile($filename, $code);
|
||||
}
|
||||
@@ -102,17 +109,21 @@ class PhpFileCache extends FileCache
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return array|false
|
||||
* @return array|null
|
||||
*/
|
||||
private function includeFileForId($id)
|
||||
private function includeFileForId(string $id) : ?array
|
||||
{
|
||||
$fileName = $this->getFilename($id);
|
||||
|
||||
// note: error suppression is still faster than `file_exists`, `is_file` and `is_readable`
|
||||
$value = @include $fileName;
|
||||
set_error_handler(self::$emptyErrorHandler);
|
||||
|
||||
$value = include $fileName;
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
if (! isset($value['lifetime'])) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user