There is a trick to get true periodic solution, i.e. u(t,x)=u(t,2pi+x) and u'(t,x)=u'(t,2pi+x). For that you have to double x-range and to choose x=0 as "source" for both boundaries.
ufunFEM =
NDSolveValue[{D[u[t, x], t] - D[u[t, x], {x, 2}] == 0,
u[0, x] == Sin[x],
PeriodicBoundaryCondition[u[t, x], x == 2 π,
Function[X, X - 2 π]],
PeriodicBoundaryCondition[u[t, x], x == -2 π,
Function[X, X + 2 π]]}, u, {t, 0, 1}, {x, -2 π, 2 π},
Method -> {"MethodOfLines",
"SpatialDiscretization" -> {"FiniteElement"}}]
Plot[ufunFEM[1, x], {x, -2 π, 2 π}, PlotRange -> All,
PlotLegends -> Automatic]

This is the same result as obtained by the tensor product grid method
ufunTPG =
NDSolveValue[{D[u[t, x], t] - D[u[t, x], {x, 2}] == 0,
u[0, x] == Sin[x], u[t, -\[Pi]] == u[t, \[Pi]]},
u, {t, 0, 1}, {x, -\[Pi], \[Pi]},
Method -> {"MethodOfLines",
"SpatialDiscretization" -> {"TensorProductGrid"}}];
Plot[ufunTPG[1, x] - ufunFEM[1, x], {x, -\[Pi], \[Pi]},
PlotRange -> All, PlotLegends -> Automatic]

For 2D case it works too
Ω = Rectangle[{-2, 0}, {2, 1}];
pde = -Derivative[0, 2][u][x, y] - Derivative[2, 0][u][x, y] ==
If[(1.25 <= x + 2 <= 1.75 || 1.25 <= x <= 1.75) &&
0.25 <= y <= 0.5, 1., 0.];
ufun = NDSolveValue[{
pde,
PeriodicBoundaryCondition[u[x, y], x == -2 && 0 <= y <= 1,
TranslationTransform[{2, 0}]],
PeriodicBoundaryCondition[u[x, y], x == 2 && 0 <= y <= 1,
TranslationTransform[{-2, 0}]],
DirichletCondition[
u[x, y] == 0, (y == 0 || y == 1) && -2 < x < 2]},
u, {x, y} ∈ Ω];
ContourPlot[ufun[x, y], {x, y} ∈ Ω,
ColorFunction -> "TemperatureMap", AspectRatio -> Automatic]

This solution is different from two ones if you choose only on target boundary
Ω1 = Rectangle[{0, 0}, {2, 1}];
ufunR = NDSolveValue[{pde,
PeriodicBoundaryCondition[u[x, y], x == 2 && 0 <= y <= 1,
TranslationTransform[{-2, 0}]],
DirichletCondition[
u[x, y] == 0, (y == 0 || y == 1) && 0 < x < 2]},
u, {x, y} ∈ Ω1];
ufunL = NDSolveValue[{pde,
PeriodicBoundaryCondition[u[x, y], x == 0 && 0 <= y <= 1,
TranslationTransform[{2, 0}]],
DirichletCondition[
u[x, y] == 0, (y == 0 || y == 1) && 0 < x < 2]},
u, {x, y} ∈ Ω1];
Row[ContourPlot[#[x, y], {x, y} ∈ Ω1,
ColorFunction -> "TemperatureMap", AspectRatio -> Automatic,
ImageSize -> 300] & /@ {ufun, ufunR, ufunL}]

In fact there is no need to double numerical domain. Just add some ghost vicinity
Ω2 = Rectangle[{-0.01, 0}, {2 + 0.01, 1}];
ufun = NDSolveValue[{
pde,
PeriodicBoundaryCondition[u[x, y], x == -0.01 && 0 <= y <= 1,
TranslationTransform[{2, 0}]],
PeriodicBoundaryCondition[u[x, y], x == 2 + 0.01 && 0 <= y <= 1,
TranslationTransform[{-2, 0}]],
DirichletCondition[
u[x, y] == 0, (y == 0 || y == 1) && -0.01 < x < 2 + 0.01]},
u, {x, y} ∈ Ω2];
ContourPlot[ufun[x, y], {x, y} ∈ Ω2,
ColorFunction -> "TemperatureMap", AspectRatio -> Automatic]

Addition comment by user21
Let's look at the limit of the ghost points to the original region size. Up until down to 10^-14. things work fine, it's only below that that the solution seems to change.
epsilon = 10^-14.;
pde = -Derivative[0, 2][u][x, y] - Derivative[2, 0][u][x, y] ==
If[(1.25 <= x + 2 <= 1.75 || 1.25 <= x <= 1.75) &&
0.25 <= y <= 0.5, 1., 0.];
\[CapitalOmega]2 = Rectangle[{-epsilon, 0}, {2 + epsilon, 1}];
ufun = NDSolveValue[{pde,
PeriodicBoundaryCondition[u[x, y], x == -epsilon && 0 <= y <= 1,
TranslationTransform[{2, 0}]],
PeriodicBoundaryCondition[u[x, y],
x == 2 + epsilon && 0 <= y <= 1, TranslationTransform[{-2, 0}]],
DirichletCondition[
u[x, y] == 0, (y == 0 || y == 1) && -epsilon < x < 2 + epsilon]},
u, {x, y} \[Element] \[CapitalOmega]2];
ContourPlot[ufun[x, y], {x, y} \[Element] \[CapitalOmega]2,
ColorFunction -> "TemperatureMap", AspectRatio -> Automatic]

Also note that if you use triangle elements you can use epsilon=0
:
epsilon = 0;
pde = -Derivative[0, 2][u][x, y] - Derivative[2, 0][u][x, y] ==
If[(1.25 <= x + 2 <= 1.75 || 1.25 <= x <= 1.75) &&
0.25 <= y <= 0.5, 1., 0.];
\[CapitalOmega]2 = Rectangle[{-epsilon, 0}, {2 + epsilon, 1}];
ufun = NDSolveValue[{pde,
PeriodicBoundaryCondition[u[x, y], x == -epsilon && 0 <= y <= 1,
TranslationTransform[{2, 0}]],
PeriodicBoundaryCondition[u[x, y],
x == 2 + epsilon && 0 <= y <= 1, TranslationTransform[{-2, 0}]],
DirichletCondition[
u[x, y] == 0, (y == 0 || y == 1) && -epsilon < x < 2 + epsilon]},
u, {x, y} \[Element] \[CapitalOmega]2,
Method -> {"FiniteElement",
"MeshOptions" -> {"MeshElementType" -> "TriangleElement"}}];
ContourPlot[ufun[x, y], {x, y} \[Element] \[CapitalOmega]2,
ColorFunction -> "TemperatureMap", AspectRatio -> Automatic]
