Typo error with namespace in costum bundle symfony2 - symfony

I'm following the tutorial "Easy Way to Transform a Twitter OAuth Library into a Symfony 2 Bundle" tuto
But not working for me, Always I got this error:
"The autoloader expected class "FEB\TwitterBundle\Api\TwitterOAuth" to be defined in file "D:\xampp\htdocs\FEB/src\FEB\TwitterBundle\Api\TwitterOAuth.php". The file was found but the class was not in it, the class name or namespace probably has a typo."
I've spent many days and I canĀ“t find out what happen. I'm sure that is a namespace issue but i'm getting crazy.
My twitterapi.php
namespace FEB\TwitterBundle\Api;
use FEB\TwitterBundle\Api\TwitterOAuth;
class TwitterApi {
protected $apiKey;
protected $apiKeySecret;
protected $apiToken;
protected $apiTokenSecret;
public function __construct($apiKey, $apiKeySecret, $apiToken, $apiTokenSecret){
$this->apiKey = $apiKey;
$this->apiKeySecret = $apiKeySecret;
$this->apiToken = $apiToken;
$this->apiTokenSecret = $apiTokenSecret;
}
public function getapiKey(){
return $this->apiKey;
}
public function getUsers($users, $url = 'users/lookup') {
$connection = new TwitterOAuth($this->apiKey, $this->apiKeySecret, $this->apiToken, $this->apiTokenSecret);
$users = $connection->get($url, array('screen_name' => $users));
return $users;
}
}
Any help, please?
Edit:
My TwitterOAuth.php
<?php
/*
* Abraham Williams (abraham#abrah.am) http://abrah.am
*
* The first PHP Library to support OAuth for Twitter's REST API.
*/
/* Load OAuth lib. You can find it at http://oauth.net */
namespace FEB\TwitterBundle\Api\Meta;
/**
* Twitter OAuth class
*/
class TwitterOAuth {
/* Contains the last HTTP status code returned. */
public $http_code;
/* Contains the last API call. */
public $url;
/* Set up the API root URL. */
public $host = "https://api.twitter.com/1.1/";
/* Set timeout default. */
public $timeout = 30;
/* Set connect timeout. */
public $connecttimeout = 30;
/* Verify SSL Cert. */
public $ssl_verifypeer = FALSE;
/* Respons format. */
public $format = 'json';
/* Decode returned json data. */
public $decode_json = TRUE;
/* Contains the last HTTP headers returned. */
public $http_info;
/* Set the useragnet. */
public $useragent = 'TwitterOAuth v0.2.0-beta2';
/* Immediately retry the API call if the response was not successful. */
//public $retry = TRUE;
/**
* Set API URLS
*/
function accessTokenURL() { return 'https://api.twitter.com/oauth/access_token'; }
function authenticateURL() { return 'https://api.twitter.com/oauth/authenticate'; }
function authorizeURL() { return 'https://api.twitter.com/oauth/authorize'; }
function requestTokenURL() { return 'https://api.twitter.com/oauth/request_token'; }
/**
* Debug helpers
*/
function lastStatusCode() { return $this->http_status; }
function lastAPICall() { return $this->last_api_call; }
/**
* construct TwitterOAuth object
*/
function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) {
$this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1();
$this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
if (!empty($oauth_token) && !empty($oauth_token_secret)) {
$this->token = new OAuthConsumer($oauth_token, $oauth_token_secret);
} else {
$this->token = NULL;
}
}
/**
* Get a request_token from Twitter
*
* #returns a key/value array containing oauth_token and oauth_token_secret
*/
function getRequestToken($oauth_callback) {
$parameters = array();
$parameters['oauth_callback'] = $oauth_callback;
$request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
return $token;
}
/**
* Get the authorize URL
*
* #returns a string
*/
function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) {
if (is_array($token)) {
$token = $token['oauth_token'];
}
if (empty($sign_in_with_twitter)) {
return $this->authorizeURL() . "?oauth_token={$token}";
} else {
return $this->authenticateURL() . "?oauth_token={$token}";
}
}
/**
* Exchange request token and secret for an access token and
* secret, to sign API calls.
*
* #returns array("oauth_token" => "the-access-token",
* "oauth_token_secret" => "the-access-secret",
* "user_id" => "9436992",
* "screen_name" => "abraham")
*/
function getAccessToken($oauth_verifier) {
$parameters = array();
$parameters['oauth_verifier'] = $oauth_verifier;
$request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
return $token;
}
/**
* One time exchange of username and password for access token and secret.
*
* #returns array("oauth_token" => "the-access-token",
* "oauth_token_secret" => "the-access-secret",
* "user_id" => "9436992",
* "screen_name" => "abraham",
* "x_auth_expires" => "0")
*/
function getXAuthToken($username, $password) {
$parameters = array();
$parameters['x_auth_username'] = $username;
$parameters['x_auth_password'] = $password;
$parameters['x_auth_mode'] = 'client_auth';
$request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
return $token;
}
/**
* GET wrapper for oAuthRequest.
*/
function get($url, $parameters = array()) {
$response = $this->oAuthRequest($url, 'GET', $parameters);
if ($this->format === 'json' && $this->decode_json) {
return json_decode($response);
}
return $response;
}
/**
* POST wrapper for oAuthRequest.
*/
function post($url, $parameters = array()) {
$response = $this->oAuthRequest($url, 'POST', $parameters);
if ($this->format === 'json' && $this->decode_json) {
return json_decode($response);
}
return $response;
}
/**
* DELETE wrapper for oAuthReqeust.
*/
function delete($url, $parameters = array()) {
$response = $this->oAuthRequest($url, 'DELETE', $parameters);
if ($this->format === 'json' && $this->decode_json) {
return json_decode($response);
}
return $response;
}
/**
* Format and sign an OAuth / API request
*/
function oAuthRequest($url, $method, $parameters) {
if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) {
$url = "{$this->host}{$url}.{$this->format}";
}
$request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
$request->sign_request($this->sha1_method, $this->consumer, $this->token);
switch ($method) {
case 'GET':
return $this->http($request->to_url(), 'GET');
default:
return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata());
}
}
/**
* Make an HTTP request
*
* #return API results
*/
function http($url, $method, $postfields = NULL) {
$this->http_info = array();
$ci = curl_init();
/* Curl settings */
curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:'));
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
curl_setopt($ci, CURLOPT_HEADER, FALSE);
switch ($method) {
case 'POST':
curl_setopt($ci, CURLOPT_POST, TRUE);
if (!empty($postfields)) {
curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
}
break;
case 'DELETE':
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
if (!empty($postfields)) {
$url = "{$url}?{$postfields}";
}
}
curl_setopt($ci, CURLOPT_URL, $url);
$response = curl_exec($ci);
$this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
$this->http_info = array_merge($this->http_info, curl_getinfo($ci));
$this->url = $url;
curl_close ($ci);
return $response;
}
/**
* Get the header info to store.
*/
function getHeader($ch, $header) {
$i = strpos($header, ':');
if (!empty($i)) {
$key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
$value = trim(substr($header, $i + 2));
$this->http_header[$key] = $value;
}
return strlen($header);
}
}
Edit2: This is the folder structure

Summing all the comments up, your TwitterOAuth.php has to be like this:
<?php
/*
* Abraham Williams (abraham#abrah.am) http://abrah.am
*
* The first PHP Library to support OAuth for Twitter's REST API.
*/
/* Load OAuth lib. You can find it at http://oauth.net */
//require_once('OAuth.php');
namespace FEB\TwitterBundle\Api;
use FEB\TwitterBundle\Api\Meta\OAuthConsumer;
use FEB\TwitterBundle\Api\Meta\OAuthRequest;
use FEB\TwitterBundle\Api\Meta\OAuthSignatureMethod_HMAC_SHA1;
use FEB\TwitterBundle\Api\Meta\OAuthUtil;
/**
* Twitter OAuth class
*/
class TwitterOAuth {
/* Contains the last HTTP status code returned. */
public $http_code;
/* Contains the last API call. */
public $url;
/* Set up the API root URL. */
public $host = "https://api.twitter.com/1.1/";
/* Set timeout default. */
public $timeout = 30;
/* Set connect timeout. */
public $connecttimeout = 30;
/* Verify SSL Cert. */
public $ssl_verifypeer = FALSE;
/* Respons format. */
public $format = 'json';
/* Decode returned json data. */
public $decode_json = TRUE;
/* Contains the last HTTP headers returned. */
public $http_info;
/* Set the useragnet. */
public $useragent = 'TwitterOAuth v0.2.0-beta2';
/* Immediately retry the API call if the response was not successful. */
//public $retry = TRUE;
/**
* Set API URLS
*/
function accessTokenURL() { return 'https://api.twitter.com/oauth/access_token'; }
function authenticateURL() { return 'https://api.twitter.com/oauth/authenticate'; }
function authorizeURL() { return 'https://api.twitter.com/oauth/authorize'; }
function requestTokenURL() { return 'https://api.twitter.com/oauth/request_token'; }
/**
* Debug helpers
*/
function lastStatusCode() { return $this->http_status; }
function lastAPICall() { return $this->last_api_call; }
/**
* construct TwitterOAuth object
*/
function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) {
$this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1();
$this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
if (!empty($oauth_token) && !empty($oauth_token_secret)) {
$this->token = new OAuthConsumer($oauth_token, $oauth_token_secret);
} else {
$this->token = NULL;
}
}
/**
* Get a request_token from Twitter
*
* #returns a key/value array containing oauth_token and oauth_token_secret
*/
function getRequestToken($oauth_callback) {
$parameters = array();
$parameters['oauth_callback'] = $oauth_callback;
$request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
return $token;
}
/**
* Get the authorize URL
*
* #returns a string
*/
function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) {
if (is_array($token)) {
$token = $token['oauth_token'];
}
if (empty($sign_in_with_twitter)) {
return $this->authorizeURL() . "?oauth_token={$token}";
} else {
return $this->authenticateURL() . "?oauth_token={$token}";
}
}
/**
* Exchange request token and secret for an access token and
* secret, to sign API calls.
*
* #returns array("oauth_token" => "the-access-token",
* "oauth_token_secret" => "the-access-secret",
* "user_id" => "9436992",
* "screen_name" => "abraham")
*/
function getAccessToken($oauth_verifier) {
$parameters = array();
$parameters['oauth_verifier'] = $oauth_verifier;
$request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
return $token;
}
/**
* One time exchange of username and password for access token and secret.
*
* #returns array("oauth_token" => "the-access-token",
* "oauth_token_secret" => "the-access-secret",
* "user_id" => "9436992",
* "screen_name" => "abraham",
* "x_auth_expires" => "0")
*/
function getXAuthToken($username, $password) {
$parameters = array();
$parameters['x_auth_username'] = $username;
$parameters['x_auth_password'] = $password;
$parameters['x_auth_mode'] = 'client_auth';
$request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
return $token;
}
/**
* GET wrapper for oAuthRequest.
*/
function get($url, $parameters = array()) {
$response = $this->oAuthRequest($url, 'GET', $parameters);
if ($this->format === 'json' && $this->decode_json) {
return json_decode($response);
}
return $response;
}
/**
* POST wrapper for oAuthRequest.
*/
function post($url, $parameters = array()) {
$response = $this->oAuthRequest($url, 'POST', $parameters);
if ($this->format === 'json' && $this->decode_json) {
return json_decode($response);
}
return $response;
}
/**
* DELETE wrapper for oAuthReqeust.
*/
function delete($url, $parameters = array()) {
$response = $this->oAuthRequest($url, 'DELETE', $parameters);
if ($this->format === 'json' && $this->decode_json) {
return json_decode($response);
}
return $response;
}
/**
* Format and sign an OAuth / API request
*/
function oAuthRequest($url, $method, $parameters) {
if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) {
$url = "{$this->host}{$url}.{$this->format}";
}
$request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
$request->sign_request($this->sha1_method, $this->consumer, $this->token);
switch ($method) {
case 'GET':
return $this->http($request->to_url(), 'GET');
default:
return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata());
}
}
/**
* Make an HTTP request
*
* #return API results
*/
function http($url, $method, $postfields = NULL) {
$this->http_info = array();
$ci = curl_init();
/* Curl settings */
curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:'));
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
curl_setopt($ci, CURLOPT_HEADER, FALSE);
switch ($method) {
case 'POST':
curl_setopt($ci, CURLOPT_POST, TRUE);
if (!empty($postfields)) {
curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
}
break;
case 'DELETE':
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
if (!empty($postfields)) {
$url = "{$url}?{$postfields}";
}
}
curl_setopt($ci, CURLOPT_URL, $url);
$response = curl_exec($ci);
$this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
$this->http_info = array_merge($this->http_info, curl_getinfo($ci));
$this->url = $url;
curl_close ($ci);
return $response;
}
/**
* Get the header info to store.
*/
function getHeader($ch, $header) {
$i = strpos($header, ':');
if (!empty($i)) {
$key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
$value = trim(substr($header, $i + 2));
$this->http_header[$key] = $value;
}
return strlen($header);
}
}
Credit to #Pazi and #Rpg600 :)

Related

How to make property nullable for Symfony Serializer

I'm trying to deserialize to an object with property which might take an array of objects as value or be null.
I have no problem deserializing arrays but I need to deserialize null to an empty array or to null itself.
For example { "items": null }
class A {
/**
* #var null|Item[]
*/
private $items = [];
/**
* #return Item[]|null
*/
public function getItems(): ?array
{
return $this->items ?? [];
}
/**
* #param Item $param
* #return A
*/
public function addItem(Item $param)
{
if (!is_array($this->items)) $this->items = [];
if (!in_array($param, $this->items))
$this->items[] = $param;
return $this;
}
// /** tried with this as well
// * #param array|null $param
// * #return A
// */
// public function setItems(?array $param)
// {
// $this->items = $param ?? [];
// return $this;
// }
/**
* #param Item $item
* #return A
*/
public function removeItem(Item $item): A
{
if (!is_array($this->items)) $this->items = [];
if (in_array($item, $this->items))
unset($this->items[array_search($item, $this->items)]);
return $this;
}
/**
* #param Item $item
* #return bool
*/
public function hasItem(Item $item): bool
{
return in_array($item, $this->items);
}
}
Serializer looks like this
$defaultContext = [
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER =>
function ($articles, $format, $context) {
return $articles->getId();
},
AbstractObjectNormalizer::SKIP_NULL_VALUES => false
];
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
$encoders = [new JsonEncoder()];
$serializer = new Serializer([
new ArrayDenormalizer(),
new DateTimeNormalizer(),
new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter, null,
new ReflectionExtractor(), null, null, $defaultContext
),
], $encoders);
$a = $serializer->deserialize('{ "items": null }', A::class, 'json');
The error I get when items is null
[Symfony\Component\Serializer\Exception\InvalidArgumentException]
Data expected to be an array, null given.
Is it possible to have nullable property?
Traced down to the Serializer source code and found three possible options to have a nullable array.
Option 1
Remove addItem, hasItem, removeItem methods and it allows to set null, array, whatever. This is less preffed solution in my case.
Option 2
Adding a constructor helps as well. https://github.com/symfony/serializer/blob/5.3/Normalizer/AbstractNormalizer.php#L381
/**
* A constructor.
* #param array|null $items
*/
public function __construct($items)
{
$this->items = $items ?? [];
}
Option 3
Extended ArrayDenormalizer and overrided denormalize method to handle nulls
public function denormalize($data, string $type, string $format = null, array $context = []): array
{
if (null === $this->denormalizer) {
throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!');
}
if (!\is_array($data) && !is_null($data)) {
throw new InvalidArgumentException('Data expected to be an array or null, ' . get_debug_type($data) . ' given.');
}
if (!str_ends_with($type, '[]')) {
throw new InvalidArgumentException('Unsupported class: ' . $type);
}
if(is_null($data))
return [];
$type = substr($type, 0, -2);
$builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null;
foreach ($data as $key => $value) {
if (null !== $builtinType && !('is_' . $builtinType)($key)) {
throw new NotNormalizableValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, get_debug_type($key)));
}
$data[$key] = $this->denormalizer->denormalize($value, $type, $format, $context);
}
return $data;
}

#Security("is_granted('remove', user)") uses not request user, symfony2

When I use the annotation #Security("is_granted('remove', user)"), I get a wrong user in Voter
/**
* Delete User by ID
*
* #Rest\Delete("/{id}", name="delete_user")
* #Security("is_granted('remove', user)")
*
* #ApiDoc(
* section="5. Users",
* resource=true,
* description="Delete User",
* headers={
* {
* "name"="Authorization: Bearer [ACCESS_TOKEN]",
* "description"="Authorization key",
* "required"=true
* }
* },
* requirements={
* {
* "name"="id",
* "dataType"="string",
* "requirement"="\[a-z\-]+",
* "description"="Id of the object to receive"
* }
* },
* output="Status"
* )
*
* #param User $user
* #return Response;
*/
public function deleteAction(User $user)
{
//$this->denyAccessUnlessGranted('remove', $user);
$em = $this->getDoctrine()->getManager();
$em->remove($user);
$em->flush();
$view = $this->view('Success deleted', Response::HTTP_NO_CONTENT);
return $this->handleView($view);
}
But, if I use functions in the body $this->denyAccessUnlessGranted('remove', $user);, it's all right. Help me to understand...
Settings
services:
user.user_voter:
class: OD\UserBundle\Security\UserVoter
arguments: ['#security.access.decision_manager']
public: false
tags:
- { name: security.voter }
Voter
namespace OD\UserBundle\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use OD\UserBundle\Entity\User;
class UserVoter extends Voter
{
const VIEW = 'view';
const EDIT = 'edit';
const REMOVE = 'remove';
protected function supports($attribute, $subject)
{
# if the attribute isn't one we support, return false
if (!in_array($attribute, array(self::VIEW, self::EDIT, self::REMOVE))) {
return false;
}
# only vote on Booking objects inside this voter
if (!$subject instanceof User) {
return false;
}
return true;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
$user = $token->getUser();
if (!$user instanceof User) {
# the user must be logged in; if not, deny access
return false;
}
switch ($attribute) {
case self::VIEW:
return $this->canView($subject, $user);
case self::EDIT:
return $this->canEdit($subject, $user);
case self::REMOVE:
return $this->canRemove($subject, $user);
}
throw new \LogicException('This code should not be reached!');
}
private function canView(User $subject, User $user)
{
if ($subject->getId() === $user->getId()) {
return true;
}
return false;
}
private function canEdit(User $subject, User $user)
{
if ($subject->getId() === $user->getId()) {
return true;
}
return false;
}
private function canRemove(User $subject, User $user)
{
if ($subject->getId() === $user->getId()) {
return true;
}
return false;
}
}
* #Security("is_granted('remove', removingUser)")
* #param User $removingUser
* #return Response;
*/
public function deleteAction(User $removingUser)
This code works well. This code works well. I did not find the confirmation, but it seems the user is reserved for the current user

oEmbed in Symfony 3 implementation

I want to allow user creating oEmbed content to my website.
There is a lot of information how to implement it but none about creating a enpoint.
Quick question: is there some ready php code samples for creating (returning) oEmbed response for third party websites?
If no, how to create it? Ex youtube link looks like this:
http://www.youtube.com/oembed?url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DiwGFalTRHDA
and the response is :
{
"html": "<iframe width=\"459\" height=\"344\" src=\"https://www.youtube.com/embed/iwGFalTRHDA?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>",
"thumbnail_height": 360,
"thumbnail_width": 480,
"provider_name": "YouTube",
"author_url": "https://www.youtube.com/user/KamoKatt",
"thumbnail_url": "https://i.ytimg.com/vi/iwGFalTRHDA/hqdefault.jpg",
"author_name": "KamoKatt",
"provider_url": "https://www.youtube.com/",
"type": "video",
"version": "1.0",
"width": 459,
"title": "Trololo",
"height": 344
}
My question is: how they know which video it is? They are using regexp to parse videoID?
What is 'best practice' for this kind of request? Should it be created as Controller, Service, Provider or how?
Finally I have created it like this:
I have created a service:
namespace AppBundle\Service;
use AppBundle\Entity\PlaceMarker;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception as Ex;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
class EmbedService
{
CONST THUMBNAIL_WIDTH = 400;
CONST THUMBNAIL_HEIGHT = 300;
CONST HTML_WIDTH = 459;
CONST HTML_HEIGHT = 344;
/**
* #var string
*/
protected $baseUrl;
/**
* #var array
*/
private $params = [];
/**
* #var EntityManager
*/
private $entityManager;
/** #var \Twig_Environment */
private $twig;
public function __construct($baseUrl, EntityManager $entityManager, \Twig_Environment $twig)
{
$this->baseUrl = strtolower($baseUrl);
$this->entityManager = $entityManager;
$this->twig = $twig;
}
/**
*
* #param Request $request
* #param null $format
* #throws \Exception
*/
public function handleRequest(Request $request, $format = null)
{
$this->params = [];
// $baseUrl = $request->getSchemeAndHttpHost();
/** handle url parameter */
$this->params['url'] = $request->query->get('url');
if (!$this->params['url']) {
throw new Ex\BadRequestHttpException('A URL parameter must be provided');
}
/** check if url matches site url */
$this->params['path'] = substr($request->query->get('url'), strlen($this->baseUrl));
if (substr(strtolower($request->query->get('url')), 0, strlen($this->baseUrl)) !== strtolower($this->baseUrl)) {
throw new Ex\BadRequestHttpException('This is not a valid URL parameter');
}
/** handle format parameters */
$this->params['format'] = $format;
if (null === $format) {
$this->params['format'] = $request->query->get('format', 'json');
}
if (!in_array($this->params['format'], ['json', 'xml'])) {
throw new \Exception('Disallowed format parameter \'' . $this->params['format'] . '\'');
}
$maxWidth = $request->query->get('maxwidth', null);
if (is_numeric($maxWidth) || is_null($maxWidth)) {
$this->params['maxWidth'] = $maxWidth;
}
$maxHeight = $request->query->get('maxheight', null);
if (is_numeric($maxHeight) || is_null($maxHeight)) {
$this->params['maxHeight'] = $maxHeight;
}
$this->params['attr'] = $this->matchRoute($this->params['path']);
}
public function matchRoute($path)
{
$route1 = new Route('/place-marker/{id}/show', ['controller' => 'PlaceMarkerController']);
$routes = new RouteCollection();
$routes->add('place-marker', $route1);
$context = new RequestContext('/');
$matcher = new UrlMatcher($routes, $context);
try {
return $matcher->match($path);
} catch (\Exception $ex) {
throw new Ex\NotFoundHttpException('No implemented!');
}
}
/**
* #return array
*/
public function create()
{
if (!$this->params) {
throw new Exception('Unable to create oEmbed. Did you use handleRequest() first?');
}
$oEmbed = [
'version' => '1.0',
'provider_url' => $this->baseUrl,
'provider_name' => 'Pixdor.com'
];
switch ($this->params['attr']['_route']) {
case 'place-marker':
/**
* #var PlaceMarker $pMarker
*/
$pMarker = $this->entityManager->getRepository(PlaceMarker::class)->find($this->params['attr']['id']);
$oEmbed['title'] = $pMarker->getName();
$oEmbed['url'] = $pMarker->getUrl();
$oEmbed['description'] = $pMarker->getDescription();
$oEmbed['caption'] = $pMarker->getCaption();
$oEmbed['html'] = $this->twig->render(':place_marker:embed.html.twig');
$oEmbed['width'] = self::HTML_WIDTH;
$oEmbed['height'] = self::HTML_HEIGHT;
$oEmbed['rich'] = 'photo';
break;
}
$oEmbed['thumbnail_width'] = isset($this->params['maxWidth']) ? $this->params['maxWidth'] : self::THUMBNAIL_WIDTH;
$oEmbed['thumbnail_height'] = isset($this->params['thumbnail_height']) ? $this->params['thumbnail_height'] : self::THUMBNAIL_HEIGHT;
$oEmbed['thumbnail_url'] = sprintf('http://placehold.it/%dx%d', $oEmbed['thumbnail_width'], $oEmbed['thumbnail_height']);
return $oEmbed;
}
/**
* Returns format type (json|xml)
* #return string
*/
public function getFormat()
{
if (!$this->params) {
throw new Exception('Unknown format type. Did you use handleRequest() first?');
}
return $this->params['format'];
}
}
services.yml
app.embed:
class: AppBundle\Service\EmbedService
arguments: ["%base_url%", "#doctrine.orm.entity_manager", "#twig"]
parameters.yml
base_url: "http://project.local"
next in the controller:
/**
* #Route("/oembed.{_format}",
* name="embed",
* defaults={"_format": null}
* )
* #param Request $request
* #param $_format
* #return \Symfony\Component\HttpFoundation\Response
* #throws \Exception
*/
public function generateAction(Request $request, $_format)
{
$oEmbed = $this->get('app.embed');
$oEmbed->handleRequest($request, $_format);
$view = View::create()
->setFormat($oEmbed->getFormat())
->setStatusCode(200)
->setData($oEmbed->create());
return $this->handleView($view);
}
you call it like this:
http://project.local/oembed?url=http://project.local/place-marker/4/show

How to call a Service in symfony2 for multiple times in one controller

I have service called "sms" that I need to use it multiple times in it's controller:
$this->get('sms')->send($userCard->getUser()->getMobile(), "text to send for first time");
$this->get('sms')->send($userCard->getUser()->getMobile(), "my second text to send");
but always it send first message twice, what should I do?
class SmsListener {
private $url="http://www.smsprovider.com/URLSend.aspx?Username=adminuser&Password=addminpassword&PortalCode=6496&Flash=0&Message=%s&Mobile=%s";
public function __construct() {
}
//put your code here
public function send($mobile,$message) {
$this->url=sprintf($this->url,$message,$mobile);
$url = str_replace(" ", '%20', $this->url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$ret=curl_exec($ch);
return $ret;
}
}
Your code breaks itself after the first call to SmsListener::send. This is because you overwrite the used url template here:
$this->url=sprintf($this->url,$message,$mobile);
After this, there are no more placeholders (%s) present. Any further call will then use the url from the first run.
You can fix this by using a local variable inside the function:
$url = sprintf($this->url, $message, $mobile);
$url = str_replace(" ", '%20', $url);
That said, you'd be better of extracting the configuration. This would actually make the service reusable. E.g.:
<?php
namespace AppBundle\Sms;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SmsListener
{
/**
* #var string
*/
private $baseUrl;
/**
* #var array
*/
private $defaultOptions;
/**
* SmsListener constructor.
*
* #param string $baseUrl
* #param array $defaultOptions
*/
public function __construct($baseUrl, $defaultOptions = [])
{
$this->baseUrl = $baseUrl;
$this->defaultOptions = $defaultOptions;
}
/**
* #param string $mobile
* #param string $message
* #param array $options
*
* #return mixed
*/
public function send($mobile, $message, array $options = [])
{
$url = $this->generateUrl(array_merge($options, [
'Message' => $message,
'Mobile' => $mobile
]));
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
return curl_exec($ch);
}
/**
* #param array $options
*
* #return string
*/
private function generateUrl(array $options)
{
return $this->baseUrl . '?' . http_build_query($this->resolveOptions($options));
}
/**
* #param array $options
*
* #return array
*/
private function resolveOptions(array $options)
{
return $this->configureOptions(new OptionsResolver())->resolve($options);
}
/**
* #param OptionsResolver $resolver
*
* #return OptionsResolver
*/
private function configureOptions(OptionsResolver $resolver)
{
return $resolver
->setDefaults($this->defaultOptions)
->setRequired(['Message', 'Mobile'])
;
}
}
Configuration:
# app/config/services.yml
services:
app.sms_listener:
class: AppBundle\Sms\SmsListener
arguments:
- "http://www.smsprovider.com/URLSend.aspx"
-
Username: "adminuser"
Password: "addminpassword"
PortalCode: "6496"
Flash: "0"
Usage (for example inside a controller):
<?php
public function someAction()
{
/** #var $srv \AppBundle\Sms\SmsListener */
$srv = $this->get('app.sms_listener');
// all defaults
$srv->send('1234', 'foobar');
// overwriting options
$srv->send('4321', 'barfoo', [
'Flash' => '1'
]);
}
generate your url inside the function like this:
class SmsListener {
public function __construct() {
}
//put your code here
public function send($mobile,$message) {
$baseUrl=$url="http://www.smsprovider.com/URLSend.aspx?Username=adminuser&Password=addminpassword&PortalCode=6496&Flash=0&Message=%s&Mobile=%s";
$url=sprintf($baseUrl,$message,$mobile);
$url = str_replace(" ", '%20', $url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$ret=curl_exec($ch);
return $ret;
}
}

symfony2 restrict model based on user - upgrade from symfony1

I'm building a multi-tenant application. In Symfony1 I would restrict access to data by accessing the user details and extending the createQuery function:
class PersonTable extends Doctrine_Table{
public function createQuery($alias = '')
{
$query = parent::createQuery($alias);
try {
$user = sfContext::getInstance()->getUser();
}catch(Exception $e){
if ($e->getMessage() == 'The "default" context does not exist.'){
return $query;
}else{
throw $e;
}
}
if ($user->hasGroup('Team1')){
//all good
}else if ($user->hasGroup('Team2')){
$user_id = $user->getGuardUser()->getStaff()->getId();
$alias = $query->getRootAlias();
$time = date('Y-m-d H:i:s',time());
$query->andWhere("$alias.type='type1' and pe.assigned_psw_id");
}
$query->orderBy('name asc');
return $query;
}
}
I know there are downsides to accessing the user object through sfContext in sf1, but this method seemed superior to others, as you can't "forget" to secure a controller against wrong user access.
How can I achieve the same in Symfony2?
I have solved this problem the following way.
Standardise how EntityRepository is fetched among controllers:
public function getUserRestrictedRepository($entity, $em = null )
{
$securityContext = $this->get( 'security.context' );
if (!$em){
$em = $this->getDoctrine()->getManager();
}
return $em
->getRepository( 'MyBundle:' . $entity )
->setSecurityContext( $securityContext );
}
Add a trait to provide queries with injected security query:
trait UserRestrictedEntityRepository {
private $securityContext;
/**
* #return mixed
*/
public function getSecurityContext()
{
return $this->securityContext;
}
/**
* #param mixed $securityContext
*/
public function setSecurityContext($securityContext)
{
$this->securityContext = $securityContext;
return $this;
}
/**
* #return mixed
*/
public function getUser()
{
return $this->getSecurityContext()->getToken()->getUser();
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
function secureQueryWithUser($alias, $qb)
{
$qb->where("1=0");
}
function appendOrderBy($qb, $orderBy)
{
$first = true;
foreach ($orderBy as $field => $dir) {
if (!$dir) $dir = 'asc';
if ($first) {
$qb->orderBy('c.' . $field, $dir);
$first = false;
}else{
$qb->addOrderBy('c.' . $field, $dir);
}
}
}
public function createUnrestrictedQueryBuilder($alias)
{
return parent::createQueryBuilder($alias);
}
/**
* Creates a new QueryBuilder instance that is prepopulated for this entity name.
*
* #param string $alias
*
* #return QueryBuilder
*/
public function createQueryBuilder($alias, $indexBy=NULL)
{
if ($this->getUser()) {
$qb = $this->_em->createQueryBuilder()
->select($alias)
->from($this->_entityName, $alias);
if (isset($this->defaultOrder) && $this->defaultOrder){
$this->appendOrderBy($qb, $this->defaultOrder);
}
if ($this->getUser()->isSuperAdmin()){
return $qb;
}else{
return $this->secureQueryWithUser($alias, $qb);
}
}else{
throw new Exception('Run setUser() before querying ' . $this->getName() .' model.');
}
}
/**
* Finds all entities in the repository.
*
* #return array The entities.
*/
public function findAll()
{
return $this->findBy(array());
}
/**
* Finds entities by a set of criteria.
*
* #param array $criteria
* #param array|null $orderBy
* #param int|null $limit
* #param int|null $offset
*
* #return array The objects.
*/
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
{
$qb = $this->createQueryBuilder('c');
foreach ($criteria as $fkey => $fval){
$qb->andWhere($fkey.' = :'.$fval);
}
if ($limit){
$qb->setMaxResults($limit);
}
if ($offset){
$qb->setFirstResult($offset);
}
$query = $qb->getQuery();
return $query->getResult();
}
}
Implement query additions based on user access in the EnityRepository
class FarmerRepository extends EntityRepository
{
use UserRestrictedEntityRepository;
private $name = 'Farmer';
private $defaultOrder = array('name' => 'asc');
function secureQueryWithUser($alias, $qb)
{
if ($this->getSecurityContext()->isGranted( 'ROLE_CLINIC_ADMIN' )) {
return $qb
->innerJoin("$alias.vet", 'v')
->innerJoin("v.clinic", "cl")
->innerJoin("cl.VetsOfClinic", "vc")
->andWhere('vc.user_id= :userid')
->setParameter('userid', $this->getUser()->getId());
}else if ($this->getSecurityContext()->isGranted( 'ROLE_VET' )){
return $qb
->innerJoin("$alias.vet", 'v')
->andWhere('v.user_id= :userid')
->setParameter('userid', $this->getUser()->getId());
}else{
return $qb
->where("$alias.user_id= :userid")
->setParameter('userid', $this->getUser()->getId());
}
}
}

Resources